Android SDKは様々なViewコンポーネント(TextView, Button, ImageViewなど)を含んでいます。
これだけで、十分に見栄えのあるアプリが開発できます。
ですが、全ての人やアプリの要望に対応することは難しく、アプリ開発中に「こんなViewが欲しい!」と思える場面があります。
そのような場合はカスタムビューの作成を検討してみましょう。「なければ作ってしまえ!」という訳です。
ここでは、「カスタムビューの属性をスタイル・テーマで指定」をまとめます。
※環境:Android Studio Flamingo | 2022.2.1
スタイルで指定
スタイルとテーマは同じものです。ただ、指定の対象が異なります。
スタイルは対象がViewです。そのViewの属性を格納し、Viewの見栄えを決めるものです。
スタイルの定義
スタイルはstyles(values/styles.xml)ファイルに、以下のようなフォーマットで定義します。
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="スタイル名"> <item name="属性名1">属性値</item> <item name="属性名2">属性値</item> : </style> </resources>
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="スタイル名(子)" parent="スタイル名(親)"> <item name="属性名1">属性値</item> <item name="属性名2">属性値</item> : </style> </resources>
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="スタイル名(親).スタイル名(子)"> <item name="属性名1">属性値</item> <item name="属性名2">属性値</item> : </style> </resources>
任意の名前が付けられます。
継承が可能です。既存のビューのスタイルを取り込んで、新たなスタイルを定義する場合に役立ちます。
「属性名」定義済みの属性名です。
以下は、stylesファイルの例です。
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="CustomViewStyle"> <item name="prm1">Prm1 is in Style.</item> <item name="prm2">20</item> </style> </resources>
スタイルの指定
以下は、レイアウトファイルの例です。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> ... <cカスタムビューのクラス名.CustomView android:id="@+id/customView" style="@style/CustomViewStyle" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> ... </androidx.constraintlayout.widget.ConstraintLayout>
属性の参照
スタイルは展開されて、定義されていた属性はattrsに含まれます。
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : View(context, attrs, defStyleAttr, defStyleRes) { private var prm1: String? private var prm2: Int init { context.obtainStyledAttributes( // 属性の取得 attrs, R.styleable.CustomView5, defStyleAttr, defStyleRes) .apply { try { prm1 = getString(R.styleable.CustomView_prm1) // 属性の参照 prm2 = getInt(R.styleable.CustomView_prm2, -1) // 属性の参照 System.out.println("CustomView5 Prm1 = ${prm1}") System.out.println("CustomView5 Prm2 = ${prm2}") } finally { recycle() // 最後にTypedArrayインスタンスを開放 } } } ... }
I/System.out: Prm1 is in Style. I/System.out: Prm2 = 20
テーマで指定
スタイルとテーマは同じものです。ただ、指定の対象が異なります。
テーマは対象がActivityです。アプリで使用可能な全Viewの属性が入り、アプリの見栄えを決めるものです。
属性の指定
以下は、テーマファイルの例です。
テーマファイルはマニフェストファイル(AndroidManifest.xml)でActivityに指定されます。
<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Theme.LibCustomView" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <!-- Primary brand color. --> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorOnPrimary">@color/white</item> <!-- Secondary brand color. --> <item name="colorSecondary">@color/teal_200</item> <item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorOnSecondary">@color/black</item> <!-- Status bar color. --> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <!-- Customize your theme here. --> <item name="prm1">Prm1 is in Theme.</item> <item name="prm2">30</item> </style> </resources>
属性の参照
テーマに指定された属性はContext#obtainStyledAttributes( )の内部で取得され、attrsと統合されます。
class CustomView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : View(context, attrs, defStyleAttr, defStyleRes) { private var prm1: String? private var prm2: Int init { context.obtainStyledAttributes( // 属性の取得 attrs, R.styleable.CustomView5, defStyleAttr, defStyleRes) .apply { try { prm1 = getString(R.styleable.CustomView_prm1) // 属性の参照 prm2 = getInt(R.styleable.CustomView_prm2, -1) // 属性の参照 System.out.println("CustomView5 Prm1 = ${prm1}") System.out.println("CustomView5 Prm2 = ${prm2}") } finally { recycle() // 最後にTypedArrayインスタンスを開放 } } } ... }
I/System.out: Prm1 is in Theme. I/System.out: Prm2 = 30
優先順位
表は属性の指定方法です。大きく分けて5つあります。
優先度 | 指定単位 | 指定対象 | 指定先 | 静・動 | |
---|---|---|---|---|---|
Immediate | 各々の属性 | View | クラスのプロパティ | ||
Layout | レイアウトファイル | ||||
Style | 複数の属性 | ||||
Theme | Activity | マニフェストファイル | |||
Default | 各々の属性 | View | クラスのプロパティ(初期値) | ||
※優先度:1(高)~5(低) |
※Immediate:「カスタムビュー作成1:Viewの継承と…」で取り上げています。
※Layout:「カスタムビュー作成2:ビューの属性定義と…」で取り上げています。
※Style:本記事で取り上げています。
※Theme:同上
※Default:Viewクラスのプロパティ初期値による動作です。
優先順位とは、同じ属性が重複して指定された場合に、採用される属性を決めるルールです。優先順位の高いものが採用されます。
関連記事: