今更遅れてDataBinding事始め(4)[双方向]
いままでのまとめ
- 今更遅れてDataBinding事始め(1)[環境構築] - exception think
- 今更遅れてDataBinding事始め(2) - exception think
- 今更遅れてDataBinding事始め(3)[include] - exception think
- 今更遅れてDataBinding事始め(3.5) - exception think
- 今更遅れてDataBinding事始め(3.7) - exception think
やっと双方向bindingを色々と試せたので、自メモとして残しておく
動作環境
Android Studio 2.3 Beta 3
メモリ 16G
双方向binding
基本的な話
レイアウトの修正
@{vm.name}
=>
@={vm.name}
ObservableXXX であればそのまま双方向に動く(ObservableBoolean とか)
これは予想通りでたしかに問題ない。
用意されているObservableオブジェクト
- android.databinding パッケージ
- ObservableInt
- ObservableBoolean
- ObservableChar
- ObservableFloat
- ObservableDouble
- ObservableLong
- ObservableShort
- ObservableByte
BaseObservableを継承しているプリミティブ型
- android.databinding パッケージ
- ObservableArrayList
- ObservableArrayMap
配列型、Map型
- android.databinding パッケージ
- ObservableField
- コレクション型を使いたい時用
- 例) ObservableField
firstName = new ObservableField<>();
- ObservableParcelable
- ObservableField
@Bindable の場合の挙動
extends BaseObservable が前提
- layout/activity_main.xml
<layout> <data> <variable type="hoge.fuga.ViewModel" name="model"/> </data> <EditText android:text="@={model.name}" /> <Button android:enabled="@{model.btnEnabled}"/> //★★
のとき、下記はなぜか動かなかった
- ViewModel(NG)
public class ViewModel extends BaseObservable { @Bindable private String name; public boolean getBtnEnabled() { return !TextUtils.isEmpty(name); } public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.model); //◎ } }
notifyPropertyChanged(BR.model); //◎ mDataBinding.executePendingBindings();//◎◎
◎◎と記載してしまうと、常に画面が初期値にリセットされてしまう。。
下記のページの記載のように、model全体ではなく、
変数単位でbindすれば notifyPropertyChangedが動くのかも?*2
- ViewModel(OK)
public class ViewModel extends BaseObservable { private String name; @Bindable public boolean getBtnEnabled() { return !TextUtils.isEmpty(name); } @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyChange();//★ } }
★であれば、下記のページ記載と同じく連動する。
ただ //★★の記載でOKのようで、特に双方向しなくていい変数に関しては*3
@={vm.name}
の記載は要らない。対象の変数を使っていれば、すべて連動するイメージ
其の場合、勿論 空のsetXXXは要らない
その他の事でなにげにハマること
String型以外を戻す時にautoキャストは動かない
public class ViewModel extends BaseObservable { private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyChange();//★ } @Bindable public int getCount() { return name.length(); } }
ただ二個目のQiita引用だとさり気なくString返却されているから気づかないのですが、★★★がエラー
- layout/activity_main.xml(NG)
<layout> <data> <variable type="hoge.fuga.ViewModel" name="model"/> </data> <EditText android:text="@={model.name}" /> <TextView android:text="@{model.count}"/> //★★★
<layout> <data> <variable type="hoge.fuga.ViewModel" name="model"/> </data> <EditText android:text="@={model.name}" /> <TextView android:text="@{String.valueOf(model.count)}"/> //★★★
なキャストはしてやる必要があり。
もしくは下記な感じか。。
<TextView android:text='@{"" + model.count}'/> //★★★
基本 “◎◎"抽出のはずなのですが、偶にパースできないみたいな記載が出る。
layout上で計算式書くとかはあまり推奨されないのかも。autoキャストも聞かないので厳密に書かないと駄目なようですし
原因的には
<TextView android:text="@{model.count}"/> //★★★
だと下記と解釈されてしまうからのよう
<TextView android:text="@{ @string/model.count[StringID] }"/> //★★★
こういうアプローチも有るけど
エラーになること自体はプリミティブ型というのが駄目なようなので
public class ViewModel extends BaseObservable { private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyChange(); } @Bindable public Integer getCount() { //◎◎◎ return name.length(); } }
//◎◎◎ とObject型を返却すれば TextView:textに設定してもエラーにはならない。
ただし、なぜか空白扱いで表示されない状態になってしまうため、痛し痒しかな・・*4
0が表示されるという記載があるんだけど、挙動が変化してるんだろうか? それとも環境依存?
参考
Viewクラスのimportをよく忘れる
<layout> <data> <import type="android.view.View"/> //△ <variable type="hoge.fuga.ViewModel" name="model"/> </data> <EditText android:id="@+id/name" android:text="@={model.name}" /> <TextView android:id="@+id/cnt" android:text="@{String.valueOf(model.cnt)}" android:visibility="@{model.name == null || model.name == '' ? View.GONE:View.VISIBLE}"/>
ちょっとだけ楽になった所
layout.xml上の補完が多少効くようになった
ただし条件的には
- layout.xmlがエラーが出ていない
- layoutでエラーが出ている状態では、補完自体が全滅になる
- GradleSyncを行っていること*5
- layout.xml上ののパーツが多くないもの
- previewを表示させながらだと、重すぎて補完候補自体が表示されないことがシバシバ*6
こうかんがえると、こまめにinclude文でlayout.xmlを分割する設計がただしいのかな? とか思ってしまったりもしますが・・
layout.xml上でid連動が出来るようになった
//△△ の記述 双方向bindingのでバックとかに便利
- id.text とかで参照できるようになる(下記だと name.text の箇所)
layout/activity_main.xml
<EditText android:id="@+id/name" android:text="@={model.name}" /> <TextView android:id="@+id/cnt" android:text="@{String.valueOf(model.cnt)}" android:visibility="@{model.name == null || model.name == '' ? View.GONE:View.VISIBLE}"/> <TextView android:text='@{"debug:" + name.text + '/' + model.cnt }'/> //△△
ただ通常のattibuteにはなくて、定義済みアトリビュートで対応しているものは、エラーになることが有る。
そのばあいは getXXX() と直接関数を書いてしまえばOK
- 例)SeekBar
- android:progress は本来は初期値のみ。これがdata-bindingでは拡張されている
<SeekBar android:progress=@={model.progress} /> <TextView android:text='@{"debug:" + model.progress }'/> //NG <TextView android:text='@{"debug:" + model.getProgress() }'/> //OK
参考
data-bindingの属性がlayoutプレビューにも反映されるようになった
- ただいまいち書ける&プレビューに反映される属性が不明。
- 補完候補に出てこなくても、動くものも有るよう
<android.support.v7.widget.Toolbar app:title="ほげほげ" //△△△ app:titleTextColor="@color/white" //△△△ android:id="@+id/toolBar" android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary">