私は、DataBindingを使用する際、xmlファイルのルートを<layout>にした上で、BindするDataクラスを必ず作成する必要があると最近まで思い込んでました。
そんなこんなで、DataBindingについて自分用おさらいメモです。
findViewByIDとDataBinding
findViewByIDとDataBindingのViewへの参照の仕組みからおさらいです。
findViewByID
- ViewHierarchyを上の階層から下の階層へ順に探索します
→ 重いです。(この重い処理が実行時に走ります)
DataBinding
- ビュー階層上で単一のパスを実行して、IDでビューを抽出します
→ 速いです。(BindingクラスがViewへの参照を保持していてくれているおかげ)
findViewByIDが遅いと言っても、少数のViewに対してでは、DataBindingと差はそんなにないです。あくまで、多数のビューに対してfindViewByIdを呼び出す場合は、DataBindingの方が速い場合があるということです。公式ドキュメント データバインディングライブラリ
DataBindingの流れ
DataBindingの仕組みをおさらいです。
DataBindingのViewへの参照で、
ビュー階層上で単一のパスを実行して、IDでビューを抽出します
と書きましたが、DataBindingのViewへの参照は、具体的には以下の流れです。
- コンパイル時にBindingクラスを自動生成
- Bindingクラスは、レイアウト内のIDを持つ各ビューに対して、参照を保持
- ActivityはBinidngオブジェクトを参照することでViewにアクセス
1. コンパイル時にBindingクラスを自動生成
コンパイル時にBindingクラスを自動生成するには、以下のようにxmlファイルのルートを<layout>にするだけでいいです。その後コンパイルすると、Bindingクラスが自動で生成されます。Bindingクラスはxmlファイル名に応じた名前で生成されてます。
例:activity_main.xml → ActivityMainBinding
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<android.support.v7.widget.AppCompatEditText
android:id="@+id/edit_text"
android:textSize="15dp"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
2. Bindingクラスは、レイアウト内のIDを持つ各ビューに対して、参照を保持する
Bindingクラスが生成されました。
生成されたBindingクラスの中身を見ると、bindさせるxmlファイルが指定されていることや、レイアウト内の ID を持つ各ビューに対して、public final フィールドが生成されていることがわかります。
DataBindingでは、ビュー階層上で単一のパスを実行して、IDでビューを抽出します。IDを持つ各ビューに対して参照を保持する、このメカニズムが findViewById を呼び出すよりも速い場合があります。
public abstract class ActivityMainBinding extends ViewDataBinding {
@NonNull
public final AppCompatEditText editText;
protected ActivityMainBinding(DataBindingComponent _bindingComponent, View _root,
int _localFieldCount, AppCompatEditText editText) {
super(_bindingComponent, _root, _localFieldCount);
this.editText = editText;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup root, boolean attachToRoot, @Nullable DataBindingComponent component) {
return DataBindingUtil.<ActivityMainBinding>inflate(inflater, com.aihana.android.databinding.R.layout.activity_main, root, attachToRoot, component);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, DataBindingUtil.getDefaultComponent());
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable DataBindingComponent component) {
return DataBindingUtil.<ActivityMainBinding>inflate(inflater, com.aihana.android.databinding.R.layout.activity_main, null, false, component);
}
public static ActivityMainBinding bind(@NonNull View view) {
return bind(view, DataBindingUtil.getDefaultComponent());
}
public static ActivityMainBinding bind(@NonNull View view,
@Nullable DataBindingComponent component) {
return (ActivityMainBinding)bind(component, view, com.aihana.android.databinding.R.layout.activity_main);
}
}
3. ActivityはBinidngオブジェクトを参照することでViewにアクセスできる
Activity側では、 binding = DataBindingUtil.setContentView(this, R.layout.activity_main) でBindingクラスのインスタンスを取得して、binding.editTextのように、xmlで定義したViewにアクセスできました。
class MainActivity : AppCompatActivity(), SampleEventHandler {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.editText.addTextChangedListener(object: CustomTextWatcher{
override fun afterTextChanged(p0: Editable?) {
if (binding.editText.text.toString().length < 8) {
binding.editText.error = "8文字以内で入力してください"
} else {
binding.editText.error = null
}
}
})
}
interface CustomTextWatcher: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
}
}
以上、DataBindingについて超基本ですがおさらいでした。