AndroidのDataBindingsで使ったTips
MVVM 設計を採用している Android 案件で、 DataBinding についていろいろ触る機会があったので調べたことなどをメモ
サンプルの全文は GitHub を参照ください。
導入
今現在の Android Studio だと、下記の設定を app の build.gradle
に下記を追加するだけで準備としては完了するようです。
1 2 3 4 5 6 7 |
android { ... dataBinding { enabled = true } } |
基本
Activity や Fragment のレイアウトを DataBinding を使ったものに変えます。
ルート要素を layout
に変更しその後に data
要素と view のルート要素を続けます。
view のルート要素は DataBinding を使わなかった場合にルートとなっていた要素です。
こんな感じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="com.ttechsoft.databindingtips.MainViewModel"/> </data> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" android:onClick="@{viewModel::onClickText}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> </android.support.constraint.ConstraintLayout> </layout> |
data
内に、その名の通り variable
でこのレイアウトファイルで参照したい変数を宣言します。
変数の内容を参照する時は、上記のように @{ }
で囲んで記載します。
そして Activity の onCreate
など通常レイアウトファイルを初期化している箇所で下記のように DataBidningUtil を用いる形に変更します。
1 2 3 4 5 6 7 8 9 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView(R.layout.activity_main) // DataBinding を使わない書き方 val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) binding.viewModel = MainViewModel() } |
binding.viewModel =
の箇所は、レイアウトファイルで宣言した variable
に実体を設定している箇所になります。
このあたりの基本は公式ドキュメントにも割としっかり書いてあるので、そちらも参照ください。
Tips
EditText の内容をコードから参照する
EditText でユーザが入力したテキストをコードの方で取得するには、@={ }
という形の構文を使います。
1 2 3 4 5 6 |
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@={viewModel.inputText}" android:layout_margin="8dp"/> |
コードの方では、String で受け取っても良いですが、今回は LiveData を使いました。
こうすることで、テキスト入力によって inputText の内容が変化した時に、自動的にこれを参照しているビューの更新も行うことができます。
1 2 3 4 5 6 |
class MainViewModel: ViewModel() { val inputText = MutableLiveData<String>() ... } |
例えば、他の TextView で @{viewModel.inputText}
で参照すると、EditText の入力に合わせて TextView の表示内容も勝手に変わる、ということが実現できます。
1 2 3 4 5 |
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.inputText}"/> |
整数値をTextViewで表示させる
整数値などを TextView で表示させる時は、String.valueOf で文字列化して渡します。整数値のまま渡すとリソース ID として処理されるようで、android.content.res.Resources$NotFoundException: String resource ID #0x0
のような例外が発生します。
1 2 3 4 5 |
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(viewModel.intValue)}"/> |
View.VISIBLE,View.INVISIBLE などを使う
visibility を使って View の表示・非表示を切り替えるということをよくやると思いますが、それを DataBinding を用いて行う場合、コードで行うのと同じように View.VISIBLE、View.INVISIBLE などの値を visibility にセットします。
ただし、View.VISIBLE などを使うには最初に xml の方に import文が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 |
<data> <import type="android.view.View"/> <variable name="viewModel" type="com.ttechsoft.databindingtips.MainViewModel"/> </data> ... <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/main_input_too_long" android:visibility="@{View.INVISIBLE}" /> |
条件分岐
条件分岐は三項演算子のような書き方が使えます。先程の visibility の設定と組み合わせて、入力文字の長さが一定以上なら TextView を表示するというコードであれば以下のようにして実現できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<data> <import type="android.view.View"/> <variable name="viewModel" type="com.ttechsoft.databindingtips.MainViewModel"/> </data> ... <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/main_input_too_long" android:layout_margin="8dp" android:visibility="@{viewModel.inputText.length() > 4 ? View.VISIBLE : View.INVISIBLE}" /> |
Contextを参照する
単に文字列を表示するだけであれば、通常の書き方で android:text="@string/main_input_too_long"
などのようにすれば良いのですが、フォーマット付きの文字列などを使いたい場合など。
xml 内で context が参照できるので、それを使います。
例えば、strings.xml でこのような文字列が定義されているとして
1 2 |
<string name="main_input_too_long">Too long. Max is %d</string> |
レイアウトの方で、context.getString
でフォーマット付きの文字列として参照できます。文字列のリソースを参照するので、R を import する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<data> <import type="android.view.View"/> <import type="com.ttechsoft.databindingtips.R"/> <variable name="viewModel" type="com.ttechsoft.databindingtips.MainViewModel"/> </data> ... <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{context.getString(R.string.main_input_too_long, viewModel.maxInput)}" android:layout_margin="8dp" android:visibility="@{viewModel.inputText.length() > viewModel.maxInput ? View.VISIBLE : View.INVISIBLE}" /> |
Glide、Picasso などでの画像の読み込み
モデルの URL などに合わせて Glide、Picasso などのライブラリを使って画像を読み込むといった場合、BindingAdapter を定義することで実現できます。
まず Kotlin、Java の方で BindingAdapter を定義します。書き方が Kotlin と Java とで違うようですが、ここでは Kotlin での書き方を載せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@BindingAdapter("imageUrl", "fallBackResource") fun loadImageUrl(view: ImageView, url: String, fallBackResource: Drawable) { Glide.with(view.context) .run { if (url.isNotEmpty()) { load(url) } else { load(fallBackResource) } } .into(view) } |
表示する画像の URL の他に、エラー時に表示する画像 fallBackResource を設定できるようにしています。
この画像は Drawable リソースで指定することを想定していますので、BindingAdapter での型も Drawable にしています。
ImageView の方では app:imageUrl
として URL 指定できます。fallBackResouce も同様です。
1 2 3 4 5 6 7 8 |
<ImageView android:layout_width="96dp" android:layout_height="96dp" android:layout_gravity="center" app:imageUrl="@{viewModel.imageUrl}" app:fallBackResource="@{@drawable/ic_launcher_background}" /> |
さいごに
DataBinding、書き方が独特な上にエラーメッセージがわかりにくいので、今度同じような案件をやるときのためにメモとして残しておきました。
確認環境
- Android Studio 3.3.1
- Zenfone3 Android 7.0
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする