前置き
2022年09月19日現在の情報です
出来るだけ最新のやり方を調べてますが、古いやり方が混入してるかも
今回作るもの
・On、Off(めんどくさいのでtrue=On false = Off )の内部情報を表示するTextView
・On、Offの情報を切り替え、自身もOn、Off情報の外部変更によって状態を変えるToggleButton
・On Offの内部情報をフリップフロップする単純なButton
上記仕様を満たすアプリ
環境
OS:Windows10
IDE:AndroidStudio
実機:Galaxy A20
言語:Kotlin
参考
Factoryクラスとかのつくり方はコレと同じ
ViewModelのつくり方とかも、これとほぼ同じ
activity_main.xmlとMainActibity.ktが違うぐらい
ファイル構成は同一なので、各クラス等がどう機能してるかは、上記の記事参照
そもそも「双方向データバインディグ」ってなんぞや
上記記事に説明と、自分が作ったわけでもない例があるが
たぶん実際に作ったほうが理解早いと思います
プロジェクト作成
AndroidStudioでEmpty Anctivityが作れる事前提で話します
パッケージ名はなんでもいいんですけど、自分はこうしてます
com.bindingtest.twowaybinding
build.gradle(app)
ここに書いてあるのとまったく同じ
MainActivityFactory.kt
ここに書いてあるのとまったく同じ
ActivityMain.ktと同じ階層に作る
package com.bindingtest.twowaybinding
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class MainActivityViewModelFactory : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create( modelClass : Class<T> ) : T
{
return MainActivityViewModel() as T
}
}
MainActivityViewModel.kt
package com.bindingtest.twowaybinding
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel
class MainActivityViewModel : ViewModel() {
val toggleButtonSw: MutableLiveData<Boolean> = MutableLiveData<Boolean>()
init {
toggleButtonSw.value = false
}
}
main_activity.xml
下記をコピペして、必要なところを都度修正
(※:これをそのままコピペしても理想通りには動きません
理由は下記で説明します)
<?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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="vm"
type="com.bindingtest.twowaybinding.MainActivityViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="409dp"
android:layout_height="354dp"
android:orientation="vertical"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.toggleButtonSw.toString()}" />
<ToggleButton
android:id="@+id/toggleButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ToggleButton"
android:checked="@{vm.toggleButtonSw}"/>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button"
android:onClick="onClick_ToggleButtonFlipButton"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
・ViewModelのフラグ情報を可視化するTextView
・ViewModeのフラグ情報を切り替え、自身もViewModelのフラグ情報を参照する
ToggleButton
・ViewModelのフラグ情報を切り替える普通のボタン
の3つで構成
MainActivity.kt
package com.bindingtest.twowaybinding
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.lifecycle.ViewModelProvider
import com.bindingtest.twowaybinding.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var factory : MainActivityViewModelFactory
lateinit var viewmodel : MainActivityViewModel
lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
factory = MainActivityViewModelFactory()
viewmodel = ViewModelProvider(this,factory).get(MainActivityViewModel::class.java)
binding.vm = viewmodel
binding.lifecycleOwner = this
}
fun onClick_ToggleButtonFlipButton( view: View )
{
viewmodel.toggleButtonSw.value = !viewmodel.toggleButtonSw.value!!
}
}
さあ実行してみよう…しかし
ビルドは通った!問題なく動いてる…
ように見えて、ToggleButtonとButtonを交互に押してみたりすると
ある問題に行き着くと思います
そう、ToogleButtonの入力が正常にViewModel側に通知されてないのです
ToggleButtonはViewModelの入力を受け取るけど
ToggleButton側からの入力はViewModelに反映しませんよと
これが「単方向バインディング」って奴です
どうすれば想定通り(双方向)になるか
答えは簡単です
<ToggleButton
android:id="@+id/toggleButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ToggleButton"
android:checked="@={vm.toggleButtonSw}"/>
現状のソースとの間違い探しみたいですが、簡単です
android:checked="@={vm.toggleButtonSw}の部分を見ればわかる通り
@と{の間に「=」を入れるだけです
これだけで双方向になります
あとは実際にビルドして、ToggleButtonとButtonを交互に押して
おかしな挙動にならず、TextViewに表示してる文字が「True」「False」を交互に表示するになれば成功です