背景付きTextViewやEditTextでautoSizeTextTypeを使う方法

# 背景付きTextViewやEditTextでautoSizeTextTypeを使う方法

>Android 8.0（API レベル 26）以上では、テキストサイズを自動的に拡大または縮小し、TextView の特性と境界に基づいてレイアウトを埋めるよう TextView に指示できます。このように設定すると、動的コンテンツを表示するさまざまな画面で、テキストサイズの最適化が容易になります。

https://developer.android.google.cn/guide/topics/ui/look-and-feel/autosizing-textview?hl=ja&msclkid=d5ecdf15c07511ecbb50c59aa695f7ef

TextViewに対して上下左右の制約を課したときに自動サイズ調節を行える機能があります。
こちら、xmlファイルに`android:autoSizeTextType="uniform"`を追加するだけで済むので非常に便利なのですが、
以下のような要件の時に問題が発生します。

- ①TextViewに背景を設定したい場合
- ②EditTextでtextのautosizingをやりたい場合

これらの状況でautosizingを設定したい場合の問題と対処法について説明します。
まず、TextViewに背景を設定したい場合は以下のように書くと思います。

```backgrounded_text.xml
<?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>
```

この書き方だと、結果は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612874/a792e54b-6981-73a6-78a6-5a9b1ae16387.png)

上下左右に絶対値の制約を課しているため文字だけに背景を設定することができません。
「じゃあ、`android:layout_width="wrap_content"`にすればいいじゃん」と思うと思いますが、そうすると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612874/dd5414e8-d0d9-4461-1a9d-408102661438.png)

このようになってしまいます。
>注: XML ファイルで自動サイズ調整を設定する場合、TextView の layout_width 属性または layout_height 属性に値「wrap_content」を使用することはおすすめしません。そのようにすると、予期しない結果が発生する可能性があります。

とある通りwrap_contentは使えません。

そこで、以下の解決法があります。

```customed_backgrounded_text.xml
<?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が効きませんので以下のように記述します。


```edit_text.xml
<?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です。

…とはなりません。
もう２点考慮しなければならないことがあります。
１つ目はViewのライフサイクルです。
どういうことかというと、Viewが実際に描画されるタイミングは`onDraw()`で、これは
Activityのサイクルでいうところの`onResume()`よりも遅いです。
なので、Activity付随のcallback関数に実装してもautosizeの大きさが確定していない段階で
呼ばれるため思った動作にならないんですね。

https://outofmem.hatenablog.com/entry/2014/04/21/120751?msclkid=50c7c427c08211eca4e9d453f75fd3d6

こちらの記事を参考にさせていただきました。

さらに、２つ目にTextViewでsetTextSize()とgetTextView()で単位が違うため変換を施さなければなりません。

https://plugout.hateblo.jp/entry/2018/08/24/001015?msclkid=507309eac11311ecac0f24ac9592d98f

こちらの記事を参考にさせていただきました。

以下のように記述すれば、目的の動作が実現できるはずです。

```MainActivity.java

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に関する記述法は[こちら](https://qiita.com/snichi/items/c7a79894238a6bea7520)をご参考ください。