PS:過去は煙のようであり、生活を分析することを理解し、同じ場所で二度もつまずかないようにしなければならない。残りの人生では、変わる必要があり、さらに積極的である必要がある。進取の心を持ち、ぼんやりと過ごさないように。
上篇は主に DataBinding の基本的な使用法についてであり、Android Jetpack コンポーネントシリーズの記事は以下の通りです:
- Android Jetpack コンポーネントの Lifecycle 編
- Android Jetpack コンポーネントの LiveData 編
- Android Jetpack コンポーネントの ViewModel 編
- Android Jetpack コンポーネントの DataBinding 編
本記事では、Binding adapters の使用方法について主に紹介します。内容は以下の通りです:
- databinding メカニズム
- BindingMethods
- BindingAdapter
- BindingConversion
databinding メカニズム#
Binding adapters は、特定の値を設定するためのフレームワークとして使用できます。databinding ライブラリは、関連する値の設定を行うための具体的なメソッドを指定することを許可します。このメソッド内でいくつかの処理ロジックを行うことができ、Binding adapters は最終的に望む結果を提供します。では、レイアウトファイルで databinding を使用してデータをバインドする際に、どのように対応するプロパティメソッドを呼び出すのでしょうか?
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}" />
レイアウトファイルで特定のデータをバインドする際、例えば上記の TextView の text プロパティでは、バインド時に自動的に互換性のある型の引数に対応するメソッド(例えば setText (arg))を受け取ります。この時、databinding ライブラリは user.getName () の戻り値の型に対応する user.setName (arg) メソッドを探します。もし user.getName () の戻り値の型が文字列であれば、String 型の引数を持つ setName (arg) メソッドが呼び出され、逆に int 型であれば Int 型の引数を持つ setName (arg) メソッドが呼び出されます。したがって、データの正確性を保証するために、XML 内の式で返される値の正確性をできるだけ確保する必要があります。もちろん、実際の必要に応じて型変換を行うこともできます。
上記の分析からわかるように、レイアウトファイルでプロパティが設定されると、databinding ライブラリは自動的に関連する setter メソッドを探して設定を行います。つまり、TextView を例にとると、特定の setter メソッドが見つかれば検証が行われます。TextView には setError (error) メソッドがあります:
@android.view.RemotableViewMethod
public void setError(CharSequence error) {
if (error == null) {
setError(null, null);
} else {
Drawable dr = getContext().getDrawable(
com.android.internal.R.drawable.indicator_input_error);
dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
setError(error, dr);
}
}
このメソッドは主にエラーメッセージを提示するために使用され、通常はコード内で使用します。ここでは、このメソッドをレイアウトファイルに設定して使用します。参考は以下の通りです:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name,default=name}"
app:error="@{user.name}"/>
以下はテスト効果の画像です:
setError (String error) メソッドがあるため、user.name が String を返すので、ここでプロパティの形で設定することができます。
BindingMethods#
これは、databinding ライブラリが提供する注釈であり、View 内の特定のプロパティが対応する setter メソッド名と一致しない場合にマッピングを行うために使用されます。例えば、TextView のプロパティ androidに対応するメソッドは setHintTextColor メソッドです。この場合、プロパティ名と対応する setter メソッド名が一致しないため、BindingMethods 注釈を使用してこのプロパティを対応する setter メソッドにバインドする必要があります。これにより、databinding はプロパティ値に基づいて対応する setter メソッドを見つけることができます。databinding は、原生 View 内のこのようなプロパティと setter メソッドの不一致の状況をすでに処理しています。TextView 内のこれらの不一致プロパティの処理をソースコードで見てみましょう。参考は以下の通りです:
@BindingMethods({
@BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),
@BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
@BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"),
@BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),
@BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),
@BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"),
})
public class TextViewBindingAdapter {
//...
}
したがって、Android フレームワーク内の View のいくつかのプロパティについて、databinding ライブラリはすでに BindingMethods を使用してプロパティの自動検索マッチングを行っています。特定のプロパティに対応する setter メソッドがない場合、databinding を使用する際にカスタム setter メソッドを作成する必要があります。この場合、BindingAdapter を使用します。
BindingAdapter#
- プロパティ設定の前処理
特定のプロパティにカスタム処理ロジックが必要な場合、BindingAdapter を使用できます。例えば、BindingAdapter を使用して TextView の setText メソッドを再定義し、入力された英語をすべて小文字に変換するカスタム TextViewAdapter は以下の通りです:
/**
* 自定义BindingAdapters
* Powered by jzman.
* Created on 2018/12/6 0006.
*/
public class TextViewAdapter {
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
//省略特殊处理...
String txt = text.toString().toLowerCase();
view.setText(txt);
}
}
この時、databinding を使用する際には、優先的に自分で定義した BindingAdapter が使用されます。なぜ認識できるのか疑問に思うかもしれませんが、コンパイル期間中に data-binding コンパイラは @BindingAdapter 注釈の付いたメソッドを探し、最終的にカスタム setter メソッドを対応する binding クラスに生成します。生成された部分コードは以下の通りです:
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
if ((dirtyFlags & 0x2L) != 0) {
// api target 1
//注意:ここは自定义のTextViewAdapter
com.manu.databindsample.activity.bindingmethods.TextViewAdapter.setText(this.tvData, "これはTextViewです");
}
}
以下は、BindingAdapter の使用を検証するためのケーススタディです。レイアウトファイルは以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--デフォルトTextView-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#a37c7c"
android:text="これはTextViewです..."
android:textSize="16sp" />
<!--dataBindingを使用したTextView-->
<TextView
android:id="@+id/tvData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#a37c7c"
android:text="@{`これはTextViewです...`}"
android:textSize="16sp" />
</LinearLayout>
</layout>
カスタム BindingAdapter の効果は以下の通りです:
カスタム TextViewAdapter が有効であり、データに対して特別な処理を簡単に行うことができることがわかります。これが BindingAdapter の役割です。
- カスタムプロパティ設定
カスタムプロパティ設定では、単一のプロパティを定義することも、複数のプロパティを定義することもできます。まずは単一のプロパティを定義します。参考は以下の通りです:
/**
* 自定义BindingAdapters
* Powered by jzman.
* Created on 2018/12/7 0007.
*/
public class ImageViewAdapter {
/**
* 定义单个属性
* @param view
* @param url
*/
@BindingAdapter("imageUrl")
public static void setImageUrl(ImageView view, String url) {
Glide.with(view).load(url).into(view);
}
}
この時、レイアウトファイル内でカスタムプロパティ imageUrl を使用できるようになります。使用例は以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<!--カスタム単一プロパティ-->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"/>
</LinearLayout>
</layout>
上記のコードのテスト効果は以下の通りです:
これで、imageUrl プロパティを使用してネットワーク画像を簡単に読み込むことができます。ここではスレッド切り替えの問題を心配する必要はありません。databinding ライブラリが自動的にスレッド切り替えを行います。では、複数のプロパティをカスタマイズするにはどうすればよいでしょうか。
以下は複数のプロパティをカスタマイズする方法で、定義方法は以下の通りです:
/**
* 自定义BindingAdapters
* Powered by jzman.
* Created on 2018/12/7 0007.
*/
public class ImageViewAdapter {
/**
* 定义多个属性
* @param view
* @param url
* @param placeholder
* @param error
*/
@BindingAdapter(value = {"imageUrl", "placeholder", "error"})
public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {
RequestOptions options = new RequestOptions();
options.placeholder(placeholder);
options.error(error);
Glide.with(view).load(url).apply(options).into(view);
}
}
この時、レイアウトファイル内で上記で定義した 3 つのプロパティ、すなわち imageUrl、placeholder、error を使用できます。使用例は以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<!--カスタム複数プロパティ-->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"
app:placeholder="@{@drawable/icon}"
app:error="@{@drawable/error}"/>
</LinearLayout>
</layout>
この時、3 つのプロパティをすべて使用することで BindingAdapter が正常に機能します。もしそのうちのいくつかのプロパティだけを使用した場合、正常にコンパイルされません。では、複数のプロパティをカスタマイズし、部分的にプロパティを正常に使用するにはどうすればよいでしょうか。@BindingAdapter 注釈には requireAll というパラメータがあり、デフォルトでは true に設定されています。これはすべてのプロパティを使用する必要があることを示します。これを false に設定すると、部分的なプロパティを正常に使用できるようになります。このため、複数のプロパティをカスタマイズする際には、@BindAdapter の requireAll 属性を false に設定する必要があります。参考は以下の通りです:
// requireAll = false
@BindingAdapter(value = {"imageUrl", "placeholder", "error"}, requireAll = false)
public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {
RequestOptions options = new RequestOptions();
options.placeholder(placeholder);
options.error(error);
Glide.with(view).load(url).apply(options).into(view);
}
この時、レイアウトファイルでは部分的なプロパティを使用できます。以下のレイアウトファイルでは imageUrl と placeholder だけを使用してもコンパイルエラーは発生しません:
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"
app:placeholder="@{@drawable/icon}"/>
BindingAdapter の紹介はここまでです。
BindingConversion#
特定の状況では、プロパティを設定する際に型間の変換が必要です。この場合、@BindingConversion 注釈を使用して型間の変換を行うことができます。例えば、androidプロパティは Drawable を受け取りますが、databinding の式で色値を設定した場合、@BindingConversion が必要です。レイアウトファイルは以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<!--型変換-->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@{true ? @color/colorRed : @color/colorBlue}"/>
</LinearLayout>
</layout>
@BindingConversion を使用して型変換を行う参考は以下の通りです:
/**
* 型変換
* Powered by jzman.
* Created on 2018/12/7 0007.
*/
public class ColorConversion {
@BindingConversion
public static ColorDrawable colorToDrawable(int color){
return new ColorDrawable(color);
}
}
上記のコードのテスト効果は以下の通りです:
@BindingConversion 注釈を使用する際は、同じ型を使用する必要があります。上記の androidプロパティは以下のようには使用できません:
<!--型変換-->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@{true ? @color/colorRed : @drawable/drawableBlue}"/>
BindingAdapter も BindingConversion も、最終的には関連コードが対応する binding クラスに生成され、指定された View にその値が設定されます。これで BindingMethods、BindingAdapter、BindingConversion に関する知識の紹介は終了です。