【v7 appcompat library を読む】 レイアウト XML のインフレート時に各種 view が compatible widget に変換される仕組み
Android アプリ開発時にお世話になる v7 appcompat library と LayoutInflater
の話です。
この記事の内容は、v7 appcompat library のバージョン 23.1.1 をもとに記述しました。
v7 appcompat library の compatible widget
v7 appcompat library には、AppCompatTextView
などの compatible widget *1 がいくつか含まれています。 いくつか下に挙げてみます。
AppCompatTextView
:TextView
の compatible widgetAppCompatButton
:Button
の compatible widgetAppCompatImageView
:ImageView
の compatible widgetAppCompatCheckBox
:CheckBox
の compatible widgetAppCompatSpinner
:Spinner
の compatible widget- などなど
これらのクラスは、新しい API level で導入された機能の一部を古いプラットフォームにも提供してくれます。 クラスによって提供される機能は違うものの、主には widget tinting 周りの機能 (+α) が提供されると考えて良さそうです。
appcompat ライブラリによる widget tinting については下のブログエントリを参照してください。
インフレート時に自動的に変換される
各 compatible widget のドキュメントを読むと、以下のような説明が書かれています。 (以下は AppCompatTextView
のもの。)
This will automatically be used when you use
AppCompatTextView | Android DevelopersTextView
in your layouts. You should only need to manually use this class when writing custom views.
つまり、レイアウト XML 中に <TextView ...>
って書いておけば、インフレート時に自動的に AppCompatTextView
になる、ってことですね。 便利です。
インフレート時に自動で変換される仕組み
便利なのはいいけどどういう仕組みなのかわかっておかないと嵌ったりすることもあるので、どういう仕組みなのか調べてみました。
LayoutInflater
に Factory をセットすることでインフレート時の挙動を変更できる
まずは LayoutInflater
について調べてみます。 インフレート時に XML ファイル中の要素名を各 view のクラスに変換する処理は、Factory (LayoutInflater.Factory
/LayoutInflater.Factory2
オブジェクト) が担っているようです。 下記メソッドを見てみると、ドキュメントにいろいろ書かれています。
AppCompatActivity
が LayoutInflater
に Factory をセットしている
AppComaptActivity#onCreate
メソッドの中を見ると、以下のように AppCompatDelegate#installViewFactory()
メソッドを呼んでいます。
getDelegate().installViewFactory();
ドキュメントには 『Installs AppCompat's LayoutInflater
Factory so that it can replace the framework widgets with compatible tinted versions』 と書かれていて、このメソッドが LayoutInflater
に独自の Factory をセットすることがわかります。 実装を追っていくと、このメソッドの中では LayoutInflaterCompat.setFactory
メソッドが呼ばれていました。
まとめ
上で調べたように、AppCompatActivity
が LayoutInflater
に独自の Factory をセットすることで、インフレート時の自動変換が実現されています。 よって、インフレーション時については以下のようにまとめられます。
AppCompatActivity
で使う限り、LayoutInflater#from(Context)
メソッドで取得できるLayoutInflater
でのインフレート時に compatible widget への自動変換がはたらくと考えて良い。AppCompatActivity
を使わないのであれば、AppCompatDelegate#installViewFactory
メソッドを使うことで使うことで同等の機能が実現される。
インフレーション時以外に何か良しなに変換してくれたりはしないので、以下のことにも気を付けましょう。 (appcompat ライブラリを使っている環境での話です。)