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クラスのプロパティ初期値による動作です。
優先順位とは、同じ属性が重複して指定された場合に、採用される属性を決めるルールです。優先順位の高いものが採用されます。
関連記事:
