ViewModelでの単方向・双方向データバインディング

こんにちは。
Androidを実装してて、ViewModelの単方向データバインディングと双方向データバインディングとでどう実装方法が違ったっけなっていうことが結構あったので、それの忘備録になります。

単方向・双方向データバインディングとは

まず、言葉の意味を見てみましょう。

単方向:ある点から他の点へ向かうさま。逆方向には向かない。
双方向:送信者から情報伝達の方向が一方向ではなく、受信側からも発信できる方式

まさに、文字の意味通りですね。
では、ViewModelのデータバインディングでいうと

  • 単方向データバインディング
    ViewModelの値変更 -> Viewへ反映

  • 双方向データバインディング
    ViewModelの値変更 -> Viewへ反映
    Viewの値変更 -> ViewModelへ反映

このような形になります。
EditTextでいうと、単方向はデータセットして表示されるが、ユーザーが更新してもViewModelは変わらない。
双方向だと、データセットしたり、ユーザーがインプットしてもView、ViewModel共に更新される。
基本的には、単方向データバインディングで問題ないことが多いと思いますが、ユーザーからのインプット等がある際は双方向データバインディングの方が使いやすそうです。

実装

ここからが本題です。
ざっくりですが、実装の差異にフォーカスして記述していきます。

導入

build.gradle(:app)

android {
    dataBinding {
        enabled true
    }
}

android内にdataBindindを有効にします。

ViewModel作成

class EditTextViewModel : ViewModel() {
    val input = MutableLiveData<String>()
}

LiveDataで実装していきます。
LiveDataのメリット↓

レイアウトファイル(.xml)

<layout 
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.sampleapp.EditTextViewModel" />
    </data>
       ~~~
</layout>

<layout>タグでレイアウトを括ります。
レイアウトファイルに上記で作成したViewModelを<data>タグ内に変数として定義します。

View

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val viewModel = EditTextViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        binding.viewModel = viewModel
        binding.lifecycleOwner = this        // LifecycleOwnerを設定
        setContentView(binding.root)
    }
}

作成したViewModelとレイアウトファイルのviewModelと紐付けます。
バインディングしたLifecycleOwnerをActivityを設定しておきます。

ここまでが共通部分になります。

単方向データバインディング

レイアウトファイル(.xml)

<EditText
    android:text="@{viewModel.input}"
    ~~~
/>

双方向データバインディング

レイアウトファイル(.xml)

<EditText
    android:text="@={viewModel.input}"
    ~~~
/>

以上です。簡単ですね。
@{viewModel.input} と @={viewModel.input} との違いですね。
結構ここの違いに気付かずに詰まった経験があります。。。

単方向データバインディングではLiveDataの使用とLifeCycleOwnerを設定は必要ないかなとも思いますが、基本的に必要な場面の方が多いと思います。
必要ない条件は下記です。

  • Viewの初期化後にデータの値を動的に変更しない場合

  • データの値が動的に変わってもViewの再描画の処理を自分で行う場合

最後に

差異自体はあまりないのですが、詰まった経験があったのとどっちがどっちか分からなくなるので忘備録として、メモ程度に単方向データバインディングと双方向データバインディングの違いを書いてみました。
データバインディングの導入方法も書いているので新たなアプリ作る際に自分自身も参考になればなっと思います。

データバインディング、好きぃ〜〜〜

いいなと思ったら応援しよう!