適切に設計されたカスタムビューは、適切に設計された他のクラスとよく似ています。使いやすいインターフェースで特定の機能セットをカプセル化し、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-auto"> <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()を呼び出して、ユーザー補助イベントを送信する - 十字キーやトラックボールなどの代替コントローラをサポートする。
ユーザー補助を考慮したビューを作成する方法については、 アプリのユーザー補助機能を強化するをご覧ください。