Androidのテーマに関するTips
これはAndroid Advent Calendar 2020 12日目の記事です。
Android開発でちょっと使えるテーマ周りのTipsを4つ紹介します!
テーマ属性として定義した色を取得する
colorPrimary
やcolorSecondary
といったテーマ属性を取得、利用する方法を紹介します。
XMLから取得
XMLから取得する方法は簡単で、?attr/attributeName
というように書くことことで、現在のテーマで定義している値を取得することができます。
<View
android:id="@+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary" />
この使い方は、既存コンポーネントのスタイル定義などでも使われているため、利用するコンポーネントのcolorやdimenの定義を追うときに覚えておくと便利です。
コードから取得
コードから取得する場合はobtainStyledAttributes
を利用します。
@ColorInt
fun Context.getThemeColor(
@AttrRes themeAttrId: Int
): Int {
return obtainStyledAttributes(
intArrayOf(themeAttrId)
).use {
it.getColor(0, Color.TRANSPARENT)
}
}
これは下記のように使います。
val colorPrimary = context.getThemeColor(R.attr.colorPrimary)
この方法を使うと、呼び出しに使ったContextに紐付いたテーマ属性を利用することができるので、ダークテーマを適用している場合は必要に応じてnight
のテーマで定義した色を使うことができるので便利です。
テーマ属性を独自に定義する
カスタム属性を定義することでテーマ属性としてかんたん理利用できます。
<!-- attrs.xml -->
<resources>
<attr name="customColor" format="color" />
<attr name="dividerColor" format="color" />
</resources>
<!-- themes.xml -->
...
<style name="Base.AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
...
<item name="customColor">?attr/colorPrimary</item>
<item name="dividerColor">?attr/colorOnBackground</item>
</style>
...
こうすることで、Viewの属性に対して定義した値を利用することができます。
<View
android:id="@+id/divider"
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="?attr/dividerColor" />
Google I/O Android Appでは、セッションリストのキーラインとしてsessionListKeyline
というカスタム属性が定義されています。
テーマの一部を置き換える
なにげなく利用している手法ですが、テーマやスタイル属性は置き換えが可能です。
<!-- themes.xml -->
...
<style name="Base.AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
...
<item name="colorPrimary">@color/app_yellow_500</item>
...
</style>
...
上記のように書くことで、parent
に指定したテーマのcolorPrimary
を置き換えています。
Google I/O Android Appでは、カスタム属性のsessionListKeyline
をセッションの詳細画面で利用するテーマで置き換えることで、同じレイアウトを使いつつキーラインのみを変えていました。
...
<!-- Non-top-level screens should inflate with this theme to replace the keyline. -->
<style name="AppTheme.Detail">
<item name="sessionListKeyline">@dimen/margin_normal</item>
</style>
...
コンテキストに紐付いたテーマを変更する
テーマはApplicationやActivityといったコンテキストに紐付いています。
ContextThemeWrapperを利用することで、コンテキストに紐付いたテーマを変更することが可能です。
たとえば、Fragmentに対してテーマを設定することはできませんが、ContextThemeWrapperを利用してlayoutInflater
を生成することで、指定のテーマでFragmentのViewを生成することができます。
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val themedInflater: LayoutInflater = if (status.isProMode) {
inflater.cloneInContext(
ContextThemeWrapper(requireContext(), R.style.Theme_Pro)
)
} else inflater
return super.onCreateView(themedInflater, container, savedInstanceState)
}
その他にも、コンテキストを使って生成するダイアログなどでもContextThemeWrapper
を利用することでテーマを置き換えることができます。
何度も紹介しているGoogle I/O Android Appでも、同様にしてアプリの詳細画面用のテーマを適用しています。
まとめ
Androidでは、テーマとスタイルを活用することでUIの構造や動作からアプリのデザインの詳細を分離できます。
注目されているJetpack Composeでも同じ概念が適用されるようなので、テーマとスタイル周りについては徐々に整理していきたいですね!
Discussion