適切に設計されたカスタムビューは、他の適切に設計されたクラスに似ています。特定の機能セットをシンプルなインターフェースでカプセル化し、CPU とメモリを効率的に使用します。カスタムビューは、適切に設計されたクラスであるだけでなく、次のことを行う必要があります。
- Android の標準に準拠する。
- Android XML レイアウトと連携する、カスタムのスタイル可能な属性を提供します。
- ユーザー補助イベントを送信する。
- 複数の Android プラットフォームと互換性がある
Android フレームワークには、これらの要件をすべて満たすビューを作成できるように、一連の基本クラスと XML タグが用意されています。このレッスンでは、Android フレームワークを使用してビュークラスのコア機能を作成する方法について説明します。
追加情報については、カスタムビュー コンポーネントをご覧ください。
ビューをサブクラス化する
Android フレームワークで定義されているすべてのビュークラスは、View
を拡張します。カスタムビューは、View
を直接拡張することも、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>
このコードは、PieChart
という名前のスタイル設定可能なエンティティに属する 2 つのカスタム属性 showText
と labelPosition
を宣言します。スタイル設定が可能なエンティティの名前は、慣例により、カスタムビューを定義するクラスと同じ名前にします。この命名規則に従う必要はありませんが、一般的なコードエディタの多くでは、この命名規則を使用してステートメントを補完しています。
カスタム属性を定義したら、組み込み属性と同様にレイアウト 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-auto"> <com.example.customviews.charting.PieChart custom:showText="true" custom:labelPosition="left" /> </LinearLayout>
長い名前空間 URI を繰り返す必要をなくすため、このサンプルでは xmlns
ディレクティブを使用しています。このディレクティブは、エイリアス custom
を名前空間 http://schemas.android.com/apk/res/com.example.customviews
に割り当てます。名前空間には任意のエイリアスを選択できます。
カスタムビューをレイアウトに追加する XML タグの名前にご注意ください。これは、カスタムビュー クラスの完全修飾名です。ビュークラスが内部クラスの場合は、さらにビューの外部クラスの名前で修飾します。たとえば、PieChart
クラスには PieView
という内部クラスがあります。このクラスのカスタム属性を使用するには、タグ com.example.customviews.charting.PieChart$PieView
を使用します。
カスタム属性を適用する
XML レイアウトからビューを作成すると、XML タグ内のすべての属性がリソース バンドルから読み取られ、AttributeSet
としてビューのコンストラクタに渡されます。AttributeSet
から直接値を読み取ることも可能ですが、次のようなデメリットがあります。
- 属性値内のリソース参照は解決されません。
- スタイルは適用されません。
代わりに、AttributeSet
を obtainStyledAttributes()
に渡します。
このメソッドは、すでに逆参照され、スタイル設定されている値の TypedArray
配列を返します。
Android リソース コンパイラは、obtainStyledAttributes()
を簡単に呼び出せるようにさまざまな処理を行います。res/
ディレクトリ内の <declare-styleable>
リソースごとに、生成される R.java
によって、属性 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()
を呼び出していることに注目してください。これらの呼び出しは、ビューが確実に動作するうえで不可欠です。プロパティになんらかの変更が加えられて外観が変わる可能性がある場合は、そのビューを無効化し、再描画の必要があることをシステムが認識できるようにする必要があります。同様に、ビューのサイズや形状に影響する可能性がある形でプロパティが変更された場合も、新しいレイアウトをリクエストする必要があります。これらのメソッド呼び出しを省略すると、見つけにくいバグが発生する可能性があります。
カスタムビューでは、重要なイベントを伝えるイベント リスナーもサポートする必要があります。たとえば、PieChart
は OnCurrentItemChanged
というカスタム イベントを公開して、ユーザーが円グラフを回転して新しいスライスにフォーカスしたことをリスナーに通知します。
プロパティとイベントの公開を忘れがちです。特に、ユーザーがカスタムビューの唯一のユーザーである場合は、公開しがちです。 時間をかけてビューのインターフェースを慎重に定義することで、今後のメンテナンス費用を削減できます。カスタムビューの表示や動作に影響するプロパティを常に公開することをおすすめします。
ユーザー補助を考慮した設計
カスタムビューは幅広いユーザーをサポートする必要があります。これには、タッチスクリーンを見たり使用したりできない障がいのあるユーザーも含まれます。障がいのあるユーザーをサポートするには、次のようにします。
android:contentDescription
属性を使用して入力フィールドにラベルを付けます。- 必要に応じて
sendAccessibilityEvent()
を呼び出して、ユーザー補助イベントを送信します。 - D-pad やトラックボールなどの代替コントローラをサポートする。
アクセス可能なビューの作成方法については、 アプリのユーザー補助機能を強化するをご覧ください。