前回 Android Architecture BluePrints を読んで挫折した(早っ)ので、基本に立ち返り最もシンプルな構成で Data Binding を体験してみます。
作るのは、MainActivity 1枚だけのミニアプリです。 純粋にリファレンスに沿って実装しました。
機能と外観
機能は、 EditText に入力した文字列をすぐ下の TextView に表示するだけのものです。
次の画像では hint を表示しているので違う文が表示されていますが、入力すると同じ文字列が表示されます。

手順
MainViewModel.ktの作成
たぶん最初に行うのが、ViewModel クラスの作成だと思います。
TextViewのtext要素にバインドさせるため、inputTextというフィールドを定義します。
class MainViewModel : ViewModel() { val inputText = MutableLiveData<String>() fun setInputText(s: String) { inputText.value = s } }
これだけ。
inputText.valueでバインドされている要素に更新通知がなされます。*1
inputTextはできればprivateにしたいところですが、後述のとおり外部からobserveメソッドでセットするのでなんとも言えないところ。BluePrints でもpublicになってるしそういうものなのかな?
activity_main.xmlの作成
MainActivity のレイアウトファイルを作成します。
ほとんどの場合 UI を Activity 上に直接載せることは少ないかと思いますが、リファレンスに沿うと Data Binding の練習はこんな形のスタートになると思います。
<layout xmlns:android=...>
<data>
<variable name="viewmodel" type="work.kcs_labo.mvvmpractice.MainViewModel" />
</data>
<android.support.constraint.ConstraintLayout ...>
<EditText
android:hint="文字を入力してくだしあ"
android:id="@+id/input" .../>
<TextView
android:hint="ここに反映されます"
android:text="@{viewmodel.inputText}"
android:id="@+id/output" .../>
</android.support.constraint.ConstraintLayout>
</layout>
Data Binding のためのレイアウトファイルでは、layout要素をルートにし、data以下を記述します。
android:text="@{viewmodel.inputText}"というところでバインドを行っています。
ここまで終わったら、念のためリビルドしActivityMainBindingクラスが自動生成されていることを確認します。
MainActivity.ktの作成
最後にMainActivityを作成します。onCreateに全部詰めた形であまり見てくれはよくないですが、最も簡単な構成になると思います。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //Viewの生成(inflate) val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) //または次も可 //val binding = ActivityMainBinding.inflate(layoutInflater) //MainViewModelの生成 val viewmodel = ViewModelProviders.of(this).get(MainViewModel::class.java) viewmodel.inputText.observe(this, Observer { input -> if (input != null) { //UI更新 binding.output.text = input } }) //BindingインスタンスにViewModelを追加 binding.viewmodel = viewmodel //TextWatcherで入力監視 input.addTextChangedListener(object : TextWatcher { ... override fun onTextChanged(s: CharSequence?, ...) { //ここでBinding#viewmodelを参照 if (s != null) { binding.viewmodel?.setInputText(s.toString()) } } }) } }
少し長いので、要素ごとに見てみます。
View の生成
//Viewの生成(inflate) val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) //または次も可 //val binding = ActivityMainBinding.inflate(layoutInflater)
もともとあったsetContentViewをDataBindingUtil.setContentView<T>に置き換えます。
簡単に言えば、Data Binding Layout File を使う場合、DataBindingUtil.setContentView<T>を用いてViewを生成します。
ViewModel の生成
次に ViewModel を生成します。
ViewModel はActivity#onCreateで生成するのが鉄則です。
//MainViewModelの生成 val viewmodel = ViewModelProviders.of(this).get(MainViewModel::class.java)
ViewModel のインスタンス化は、ViewModelProvidersの静的メソッドにより生成します。
値更新時の動作の実装
次に、inputText (MutableLiveData型)のsetValueメソッドが呼ばれた(=値が更新された)ときの動作を定義します。
viewmodel.inputText.observe(this, Observer { input -> if (input != null) { //UI更新 binding.output.text = input } })
binding.outputは TextView です。text要素を入力された値(input)に更新します。
これにより、「何らかの動作」でinputText.setValueがコールされたとき、Observerの匿名メソッドが読まれてoutputが更新されます。
「何らかの動作」部分は、TextWatcher にて実装します。(後述)
Binding に ViewModel をセット
次に、View と ViewModelを紐づけます。
これによりbinding.viewmodel?.[メソッド]といった形で、ViewModel のメソッドがコールできるようになります。View への処理を ViewModel に委譲することができます。*2
//BindingインスタンスにViewModelを追加
binding.viewmodel = viewmodel
「何らかの動作」の実装
今回は EditText を入力として使っているので、EditText に TextWatcherを追加して随時イベントが発生するようにしてみます。*3
//TextWatcherで入力監視 input.addTextChangedListener(object : TextWatcher { ... override fun onTextChanged(s: CharSequence?, ...) { //ここでActivityMainBinding#viewmodelを参照 if (s != null) { binding.viewmodel?.setInputText(s.toString()) } } })
binding.viewmodel?.setInputTextにより次のメソッドが呼ばれます。(再掲)
class MainViewModel : ViewModel() { val inputText = MutableLiveData<String>() fun setInputText(s: String) { inputText.value = s } }
これで入力に応じて随時同じ文字列が出力されるようになりました。長い。
制御の流れ
文字列を入力する
↓
TextWatcher のbinding.viewmodel?.setInputText(s.toString())が呼ばれる
↓
inputText.value = sが読まれる
↓
更新通知
↓
Observerの匿名メソッドが読まれる
↓
binding.output.text = inputでUI更新
流れは上記のようになりますが、、、うーん分かりづらい。
次回は少し発展させて Activity に Fragment 載せた形で実装してみようと思いますが、この文章力で書き続けていいものか不安すぎる(´・ω・`)