背景付きTextViewやEditTextでautoSizeTextTypeを使う方法
Android 8.0(API レベル 26)以上では、テキストサイズを自動的に拡大または縮小し、TextView の特性と境界に基づいてレイアウトを埋めるよう TextView に指示できます。このように設定すると、動的コンテンツを表示するさまざまな画面で、テキストサイズの最適化が容易になります。
TextViewに対して上下左右の制約を課したときに自動サイズ調節を行える機能があります。
こちら、xmlファイルにandroid:autoSizeTextType="uniform"
を追加するだけで済むので非常に便利なのですが、
以下のような要件の時に問題が発生します。
- ①TextViewに背景を設定したい場合
- ②EditTextでtextのautosizingをやりたい場合
これらの状況でautosizingを設定したい場合の問題と対処法について説明します。
まず、TextViewに背景を設定したい場合は以下のように書くと思います。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingVertical="2dp"
android:paddingHorizontal="15dp"
android:text="example"
android:autoSizeTextType="uniform"
android:textColor="@color/white"
android:background="@drawable/black_edge_rounded"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
この書き方だと、結果は以下のようになります。
上下左右に絶対値の制約を課しているため文字だけに背景を設定することができません。
「じゃあ、android:layout_width="wrap_content"
にすればいいじゃん」と思うと思いますが、そうすると
このようになってしまいます。
注: XML ファイルで自動サイズ調整を設定する場合、TextView の layout_width 属性または layout_height 属性に値「wrap_content」を使用することはおすすめしません。そのようにすると、予期しない結果が発生する可能性があります。
とある通りwrap_contentは使えません。
そこで、以下の解決法があります。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/constraint"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:paddingVertical="2dp"
android:paddingHorizontal="15dp"
android:text="example"
android:textColor="@color/white"
android:background="@drawable/black_edge_rounded_ripple"
android:maxLines="1"
android:gravity="center_vertical"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/dummy_text_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingVertical="2dp"
android:paddingHorizontal="15dp"
android:text="example"
android:visibility="invisible"
android:autoSizeTextType="uniform"
android:textColor="@color/white"
android:background="@drawable/black_edge_rounded_ripple"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
dummy_text_view
をtext_view
を表示させたい大きさの制約と同じところに設定したうえで、
android:autoSizeTextType="uniform"
と記載します。これで、text_view
はwrap_content
のまま、
text_view
に表示させたい文字の大きさをdummy_text_view
に持たせることができます。
EditTextの場合はandroid:layout_width="0dp"
android:layout_height="0dp"
を設定してもautosizingが効きませんので以下のように記述します。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/constraint"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edit_text"
android:layout_width="0dp"
android:layout_height="0dp"
android:inputType="text"
android:text="example"
android:background="@null"
android:textColor="@color/white"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/dummy_text_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingVertical="2dp"
android:paddingHorizontal="15dp"
android:text="example"
android:visibility="invisible"
android:autoSizeTextType="uniform"
app:layout_constraintHeight_percent="0.1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
あとはJava/Kotlin codeの方で
textView.setTextSize(dummyTextView.getTextSize())
を呼んでやればOKです。
…とはなりません。
もう2点考慮しなければならないことがあります。
1つ目はViewのライフサイクルです。
どういうことかというと、Viewが実際に描画されるタイミングはonDraw()
で、これは
ActivityのサイクルでいうところのonResume()
よりも遅いです。
なので、Activity付随のcallback関数に実装してもautosizeの大きさが確定していない段階で
呼ばれるため思った動作にならないんですね。
こちらの記事を参考にさせていただきました。
さらに、2つ目にTextViewでsetTextSize()とgetTextView()で単位が違うため変換を施さなければなりません。
こちらの記事を参考にさせていただきました。
以下のように記述すれば、目的の動作が実現できるはずです。
public float convertPxToSp(float px) {
return px / this.getResources().getDisplayMetrics().scaledDensity;
}
editText.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
editText.setTextSize(convertPxToSp(binding.dummyTextView.getTextSize()));
}
});
お疲れさまでした。
この記事で使っているレスポンシブUIに関する記述法はこちらをご参考ください。