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 DevelopersTextViewin 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 ライブラリを使っている環境での話です。)