findViewByIDとDataBinding, DataBindingのおさらい

私は、DataBindingを使用する際、xmlファイルのルートを\<layout>にした上で、BindするDataクラスを必ず作成する必要があると最近まで思い込んでました。:sweat_smile:
そんなこんなで、DataBindingについて自分用おさらいメモです。

# findViewByIDとDataBinding
findViewByIDとDataBindingのViewへの参照の仕組みからおさらいです。

### findViewByID
- ViewHierarchyを上の階層から下の階層へ順に探索します
    
→　重いです。（この重い処理が実行時に走ります）

### DataBinding
- ビュー階層上で単一のパスを実行して、IDでビューを抽出します
       
→　速いです。（BindingクラスがViewへの参照を保持していてくれているおかげ）

findViewByIDが遅いと言っても、少数のViewに対してでは、DataBindingと差はそんなにないです。あくまで、多数のビューに対してfindViewByIdを呼び出す場合は、DataBindingの方が速い場合があるということです。[公式ドキュメント　データバインディングライブラリ](https://developer.android.com/topic/libraries/data-binding/?hl=ja)

# DataBindingの流れ
DataBindingの仕組みをおさらいです。

DataBindingのViewへの参照で、
**ビュー階層上で単一のパスを実行して、IDでビューを抽出します**
と書きましたが、DataBindingのViewへの参照は、具体的には以下の流れです。

1. コンパイル時にBindingクラスを自動生成
2. Bindingクラスは、レイアウト内のIDを持つ各ビューに対して、参照を保持
3. ActivityはBinidngオブジェクトを参照することでViewにアクセス

## 1. コンパイル時にBindingクラスを自動生成
コンパイル時にBindingクラスを自動生成するには、以下のようにxmlファイルのルートを\<layout>にするだけでいいです。その後コンパイルすると、Bindingクラスが自動で生成されます。Bindingクラスはxmlファイル名に応じた名前で生成されてます。
例：activity_main.xml → ActivityMainBinding

```xml:activity_main.xml
<?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 を呼び出すよりも速い場合があります。

```kotlin:ActivityMainBinding.kt
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にアクセスできました。

```kotlin:MainActivity.kt
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について超基本ですがおさらいでした。