適切に設計されたカスタムビューは、適切に設計された他のクラスとよく似ています。使いやすいインターフェースで特定の機能セットをカプセル化し、CPU とメモリを効率的に使用できるなど、さまざまなメリットがあります。ただし、カスタムビューは、適切に設計されたクラスであるだけでなく、以下の要件を満たす必要があります。
- Android 標準に準拠している
- Android XML レイアウトで機能する、カスタムのスタイル設定が可能な属性を提供する
- ユーザー補助イベントを送信する
- 複数の Android プラットフォームと互換性がある
Android フレームワークには、上記の要件をすべて満たすビューの作成に役立つ基本クラスと XML タグのセットが用意されています。このレッスンでは、Android フレームワークを使用してビュークラスの中核的機能を作成する方法について説明します。
関連情報については、カスタム コンポーネントをご覧ください。
ビューをサブクラス化する
Android フレームワーク内で定義されているビュークラスは、すべて View
を拡張します。カスタムビューは、View
を直接拡張することも、既存のビュー サブクラスの 1 つ(Button
など)を拡張して時間を節約することもできます。
Android Studio でビューを操作できるようにするには、最低限、Context
オブジェクトと AttributeSet
オブジェクトをパラメータとして取るコンストラクタを用意する必要があります。このコンストラクタにより、Layout Editor でビューのインスタンスを作成、編集することが可能になります。
Kotlin
class PieChart(context: Context, attrs: AttributeSet) : View(context, attrs)
Java
class PieChart extends View { public PieChart(Context context, AttributeSet attrs) { super(context, attrs); } }
カスタム属性を定義する
ビルトイン View
をユーザー インターフェースに追加するには、XML 要素内でビューを指定して、要素の属性を使用してビューの外観や動作を制御します。また、適切に作成されたカスタムビューであれば、XML を通じて追加し、スタイルを設定することができます。カスタムビュー内でこの動作を有効にするには、次のようにセットアップする必要があります。
<declare-styleable>
リソース要素内でビューのカスタム属性を定義する- XML レイアウト内で属性の値を指定する
- 実行時に属性値を取得する
- 取得した属性値をビューに適用する
このセクションでは、カスタム属性を定義して、その値を指定する方法について説明します。次のセクションでは、実行時に値を取得して適用する方法について説明します。
カスタム属性を定義するには、プロジェクトに <declare-styleable>
リソースを追加します。このリソースは通常、res/values/attrs.xml
ファイル内に配置します。attrs.xml
ファイルの例を以下に示します。
<resources> <declare-styleable name="PieChart"> <attr name="showText" format="boolean" /> <attr name="labelPosition" format="enum"> <enum name="left" value="0"/> <enum name="right" value="1"/> </attr> </declare-styleable> </resources>
このコードは、showText
と labelPosition
という 2 つのカスタム属性を宣言しています。また、どちらの属性も、スタイル設定が可能な PieChart
という名前のエンティティに属しています。一般的に、スタイル設定が可能なエンティティの名前は、カスタムビューを定義するクラスの名前と同じにします。この慣例に厳密に従う必要はありませんが、一般的なコードエディタの多くは、この命名規則に依存してステートメントを補完します。
カスタム属性を定義したら、レイアウト XML ファイルで、それらを組み込み属性と同様に使用できます。唯一の相違点として、カスタム属性は、別の名前空間に属しています。具体的には、http://schemas.android.com/apk/res/android
名前空間ではなく、http://schemas.android.com/apk/res/[your package name]
名前空間に属しています。たとえば、PieChart
用に定義した属性は、次のように使用します。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"> <com.example.customviews.charting.PieChart custom:showText="true" custom:labelPosition="left" /> </LinearLayout>
長い名前空間 URI を繰り返す手間を省くため、このサンプルでは、xmlns
ディレクティブを使用しています。このディレクティブは、http://schemas.android.com/apk/res/com.example.customviews
名前空間に custom
エイリアスを割り当てています。名前空間に対して、任意のエイリアスを選択できます。
カスタムビューをレイアウトに追加する XML タグの名前にご注意ください。これは、カスタム ビュークラスの完全修飾名です。ビュークラスが内部クラスである場合は、さらにビューの外部クラスの名前で修飾する必要があります。たとえば、PieChart
クラスには、PieView
という内部クラスがあります。このクラスのカスタム属性を使用する場合は、com.example.customviews.charting.PieChart$PieView
というタグを使用します。
カスタム属性を適用する
XML レイアウトからビューが作成されると、XML タグ内のすべての属性がリソース バンドルから読み取られ、AttributeSet
としてビューのコンストラクタに渡されます。直接 AttributeSet
から値を読み取ることも可能ですが、次のようなデメリットがあります。
- 属性値内のリソース参照が解決されない
- スタイルが適用されない
この方法の代わりに、AttributeSet
を obtainStyledAttributes()
に渡します。このメソッドは、すでに参照解除されスタイル設定されている値の TypedArray
配列を返します。
Android リソース コンパイラは、obtainStyledAttributes()
の呼び出しを容易にするために、さまざまな処理を行います。生成された R.java は、res ディレクトリ内の <declare-styleable>
リソースごとに、属性 ID の配列と、配列内の各属性のインデックスを定義する定数のセットを定義します。アプリでは、事前定義済みの定数を使用して、TypedArray
から属性を読み取ります。PieChart
クラスがその属性を読み取る方法を次のスニペットに示します。
Kotlin
init { context.theme.obtainStyledAttributes( attrs, R.styleable.PieChart, 0, 0).apply { try { mShowText = getBoolean(R.styleable.PieChart_showText, false) textPos = getInteger(R.styleable.PieChart_labelPosition, 0) } finally { recycle() } } }
Java
public PieChart(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.PieChart, 0, 0); try { mShowText = a.getBoolean(R.styleable.PieChart_showText, false); textPos = a.getInteger(R.styleable.PieChart_labelPosition, 0); } finally { a.recycle(); } }
なお、TypedArray
オブジェクトは共有リソースであり、使用後にリサイクルする必要があります。
プロパティとイベントを追加する
属性はビューの動作と外観を制御するための強力な方法ですが、属性を読み取ることができるのはビューが初期化されたときだけです。動的な動作を実現するには、各カスタム属性のプロパティ ゲッターとプロパティ セッターのペアをエクスポーズします。PieChart
が showText
というプロパティをエクスポーズする方法を次のスニペットに示します。
Kotlin
fun isShowText(): Boolean { return mShowText } fun setShowText(showText: Boolean) { mShowText = showText invalidate() requestLayout() }
Java
public boolean isShowText() { return mShowText; } public void setShowText(boolean showText) { mShowText = showText; invalidate(); requestLayout(); }
setShowText
は、invalidate()
と requestLayout()
を呼び出しています。この 2 つの呼び出しは、ビューを確実に動作させるために重要です。外観を変える可能性があるプロパティを変更した後は、ビューを再描画する必要があることをシステムに伝えるために、ビューを無効化する必要があります。同様に、ビューのサイズまたは形状に影響する可能性があるプロパティを変更したときは、新しいレイアウトをリクエストする必要があります。これらのメソッドの呼び出しを忘れると、検出が困難なバグが発生することがあります。
また、カスタムビューは、重要なイベントを通知するイベント リスナーもサポートする必要があります。たとえば、PieChart
は、OnCurrentItemChanged
というカスタム イベントをエクスポーズすることにより、ユーザーが円グラフを回転させて新しいスライスにフォーカスしたことをリスナーに通知します。
特に、カスタムビューを使用するアプリがほかにない場合、プロパティとイベントをエクスポーズすることを忘れがちです。時間をかけてビューのインターフェースを慎重に定義することにより、将来のメンテナンス コストを削減することができます。カスタムビューの外観や動作に影響するプロパティは、常にエクスポーズすることをおすすめします。
ユーザー補助を考慮して設計する
カスタムビューは、幅広いユーザーをサポートしなければなりません。そのようなユーザーには、タッチスクリーンを見られない、または使用できない障がいを持つ人々が含まれます。障がいを持つユーザーをサポートするため、次のようにセットアップするようにしてください。
android:contentDescription
属性を使用して入力フィールドにラベルを付ける- 必要に応じて、
sendAccessibilityEvent()
を呼び出して、ユーザー補助イベントを送信する - 十字キーやトラックボールなどの代替コントローラをサポートする
ユーザー補助を考慮したビューを作成する方法については、Android デベロッパー ガイドのアプリのユーザー補助機能を強化するをご覧ください。