くうと徒然なるままに

モバイルアプリを作りながらバックエンドも作っています。

ContextThemeWrapper について

ContextThemeWrapperとは

任意のContextのThemeを上書きしたContextを作成することができるものです。

何ができるのか

Viewの作成などを行う場合にContextThemeWrapper を使い作成したContextを渡すことで作成時点に指定した任意のThemeを利用してViewを作ることができるようになります。

実際に使う場合には以下のように作成したContextThemeWrapperのインスタンスを LayoutInflaterなどに渡してあげることで自分の思うままのThemeでViewを作成することができます。
※ソースコード的にはFragmentやActivityでも似たようなことはできるかもですが、もっと簡単な方法があるので普通はしないと思う...

f:id:kuxumarin:20200716224356p:plain

fun createViewAsSpecificTheme(context: Context, root: ViewGroup?): View {
    val resourceFile = R.layout.activity_main
    val wrappedContext = ContextThemeWrapper(context, R.style.Theme_MaterialComponents_DayNight)
    return LayoutInflater.from(wrappedContext).inflate(resourceFile, root)
}

実際に使われている場所

仕事のコードで使ったのですがそのコードを出すわけにもいかないので今回は公開されているコードを使います。

端的に言ってしまうとAlertDialog のThemeを切り替えるところで使われています。 ※他にも使われている場所はたくさんありますが、説明のために省きます。

AlertDialog の Themeを切り替える機能を有効活用しているものを見ながらだとわかりやすいかなと思います。

Material Component for Android

Material Design を Android で実現するために使われているライブラリですね。 今回はこれを題材に見ていきます。

MaterialAlertDialog

マテリアルデザインの中にある ダイアログを表示するためのものです。

具体的には以下の写真を見ればなんとなくわかるかと思います。

f:id:kuxumarin:20200716223828p:plain

具体的にはどのように使われているのか

MaterialAlertDialog の場合は 一般的な AlertDialog を表示するだけではなくMaterialDesign の Theme を適用した状態のAlertDialog を表示する必要があります。

MaterialAlertDialogのConstructor を呼び出すと内部的には AlertDialog のConstructor を Material Design の Theme を引数に追加した状態で呼び出します。

具体的には、以下のAlertDialogBuilder の static constructor を呼び出しています。

AlertDialog.Builder#Builder(Context context, int themeResId)

この時この関数内では 引数で渡ってきた contextのThemeを第二引数に渡ってきた ThemeResId の Themeに ContextThemeWrapper で上書きして AlertDialog を作成することによ任意のThemeで作成できるようになっています。

まとめ

今回の例では極端に自由度の高いコードを題材にしてしまったためちょっとわかりづらいかもしれないですが、ContextのThemeをContextThemeWrapperを使えば自由に上書きできることがわかったかなと思います。