問: Android の TextView の色はどこで何が指定されているか答えなさい
はじめに
問: TextView の色はどこで何が指定されているか答えなさい (Themeは Theme.AppCompat.NoActionBar が使用されているものとする)
という問題に答えるにはどう調べればよいのか、というメモ。
調べ方を知るために、そもそも Android の View はどのように見た目が定義されているのか理解するところから始める。
結論だけを知りたい人は記事の後半を読んでください。
実行環境
- macOS Catalina (10.15.7)
- Android Studio 4.0.1
色はどこで何が指定されているのか
まずは色という情報はどのように整理されているのかをおさらいする。
Android には Theme と Style というものがある。
Theme には colorPrimary や colorButtonNormal といった 属性[1] に、どのような値を設定するかを記述する。これらの属性はプログラミング言語でいう変数のような機能を持つ。
Style はある View の属性[2]を共通化するために使う。属性の値は固定値でもいいし、@dimen/xxxや@string/yyyでもいいし、Themeで設定した属性を参照する ?attr/colorPrimary
を使ってもよい。?attr/colorPrimary
と書くと Theme で設定した colorPrimary
の値を取得し展開することができる。さきほど Theme は変数のようなものだと喩えたのはこのためだ。
Theme と Style の関係をざっくり表すと、 Themeは変数を作成して初期化する場所で、Style はその変数のいずれかを使う場所となる。
そして色の情報は、ほぼほぼ Theme に集約されており、 colorPrimary や colorButtonNormal といった役割に対して名前がつけられている。
TextView や Button や DialogFragment など、Android の View の属性にはデフォルト値が設定されており、それらは Style に記述されている。
どの Style に記述されているのか調べるには、Viewのコンストラクタから追いかけていくと見つけられる。
例えば TextView の場合、
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
の com.android.internal.R.attr.textViewStyle
から、 textViewStyle
という attr が使われていることがわかる。
次は、現在使用している Theme で textViewStyle
を記述しているところを探せば良い。といってもググったりファイル検索をするのでは大量に見つかるので、Android Studio の定義元ジャンプを使うのが確実。使用している Theme にカーソルを合わせて、右クリック -> Go To -> Implementation でジャンプできる。
するとテーマの定義元にジャンプするが、 textViewStyle
が設定されているようには見えない。
Style は .
で継承できるため、この場合 Theme.AppCompat.NoActionBar
は Theme.AppCompat
を継承していることになる。継承元の Theme.AppCompat
を調べるためにまた定義元にジャンプして調べる。さすがに手間なのでキーボードショートカットを覚えよう。[3]
調べているとジャンプ先を選ぶ画面が表示されることがある。
こういう時は、詳しくないうちは一番下から調べていくと確実。これはほぼ、v28 は v26 を、v26 は v23 をベースにしていて、最終的には網羅して調べることになる。[4]
そうして調べていくと、ようやく textViewStyle
に指定されているものが見つかる。
Widget.AppCompat.TextView
の定義元にジャンプしていくことを繰り返していくと、 今度は textAppearance
の定義が見つかる。
textAppearance は特殊なもので Style の Text 特化版といえる。textAppearance には style を指定することで、textから始まるもの (textColor や textSize など) をまとめて管理することができる。
さて、ここでは ?attr/textAppearanceSmall
となっているが、?attr/
ということは、 Themeで設定した変数のようなもの textAppearanceSmall
を参照している、ということになる。つまり textAppearanceSmall
の実体は何なのか、また Theme から探らなければならない。
再び使用しているテーマから定義元ジャンプを繰り返していき、 textAppearanceSmall
は @style/TextAppearance.Material.Small
だと突き止める。
そして @style/TextAppearance.Material.Small
の中で、 textColor
は ?attr/textColorTertiary
が使用されているのだとたどり着く。
もちろん ?attr
が使われているということでまだ先がある。
再びテーマから定義元ジャンプを繰り返して textColorTertiary
が定義されている場所を特定。
@color/abc_secondary_text_material_dark
の定義元へジャンプし、最終的に #36ffffff
という暗い透明な白という色が使われていると分かった。
長旅だった。
学び
この長旅を経て、TextView の textColor を抽象化しているものが理解できる。
抽象度の低い順から、
- @color/secondary_text_disabled_material_dark
- @color/abc_secondary_text_material_dark
- android:textColorTertiary
- @style/TextAppearance.Material.Small
- textAppearanceSmall
- textAppearance
- textViewStyle
となる。
例えば secondary_text_disabled_material_dark
を変更すれば、特定のテーマの文字色を変更でき、 textViewStyle
を変更すれば TextView のデフォルトの Style 自体を変更できる。
その知見だけではあまり通常の業務に活かされないが(TextViewの文字色を変更するなら素直に textColor
を設定すれば良いから)他の属性の初期値も同じ手順で調べられるので、思ったとおりのデザインが実装できない時の調査時には役に立つ。特にDialogFragmentではめっちゃ役に立つ(実体験)。
Discussion