LoginSignup
0
1

More than 1 year has passed since last update.

Google codelabs Android DataBinding のコードのつながりをふわ~っと確認

Posted at

最近、Databindingを理解するために、Google codelabs Android DataBindingで学習してみたのですが、思考停止で書かれている通りに実装したところ、Databindingは実装できたものの、どのコードと、どのコードが関連して動作しているのかが、いまいちピンときていなかったので、コード同士のつながりを確認することにしました。
この記事はその確認結果のまとめです。

Google codelabs Android DataBindingで作るもの

このCodelabsでは以下のようなものを作ります。(ほぼ完成しているものが準備されている)
アプリ動作.gif

LIKEボタンをポチポチするとLikesの数字が増えて、Likesが一定以上になると、プログレスバーとアイコンが変化します。
最初から準備されているコードは、DataBindingを使用せずにこの挙動を実現している状態なので、それを修正してDataBindingで実現する、というのがこのCodelabsの主旨です。

Codelabsの内容

全力でサボりますが、こちらの記事で全編内容が紹介されています。分かりにくい部分(Codelabs内のケアレスミスとか)も注釈として書かれてるので参考になりました。

コードのつながりを読み解く

読み解くというほどのものでもないかもですが、読み解きます笑
※ここで扱うコードは、Codelabsの最初に提供されるものではなく、Codelabsが完了した段階でのものです。

かなりざっくり言うと、このアプリは
・PlainOldActivity.kt
・SimpleViewModel.kt
・BindingAdapter
・plain_activity
で構成されています。

これらの中から、DataBindingの構成要素を独断と偏見で抜き出すと、こんな概略図になります。(いろいろ省略してます)

主要クラス構成図png.png
これらのつながりを、ざっくり見ていきます。


まず、plain_activity.xml内でレイアウト変数(layout variables)が定義されています。

plain_activity.xml
   <data>
       <variable
            name="viewmodel"
            type="com.example.android.databinding.basicsample.data.SimpleViewModel"/>
   </data>

nameはviewmodelとなっていて、
typeは"com.example.android.databinding.basicsample.data.SimpleViewModel"
を指定しており、これはSimpleViewModel.ktのことを指しています。

なので意味的には、「viewmodelは、SimpleViewModel.ktのことを指す」という具合になりそうです。

続いてタグ内で
android:text="@{}"というような表記 (layout expression、レイアウト式) が出てきます。
ここにdataタグ内で記述したレイアウト変数が入ります。
こんな感じ↓

plain_activity.xml
<TextView
            android:id="@+id/plain_name"
            android:text="@{viewmodel.name}"    
以下省略     
/>

ここでdataタグ内のnameで定義した、viewmodel@{viewmodel.name}という形で記述されています。
これはデータの紐付け先を示しています。

viewmodelはSimpleViewModel.kt(com.example.android.databinding.basicsample.data.SimpleViewModel)を型として指定していたので、「SimpleViewModel.ktからnameを引っ張って来い」というように読めます。

ただ、plain_activity.xmlからSimpleViewModel.ktへ直接紐付けされているわけでありません。
PlainOldActivity.ktは以下のようになっています。

PlainOldActivity.kt
class PlainOldActivity : AppCompatActivity() {

    // Obtain ViewModel from ViewModelProviders
    private val viewModel by lazy { ViewModelProviders.of(this).get(SimpleViewModel::class.java) }

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

        //➀
        val binding : PlainActivityBinding =
                DataBindingUtil.setContentView(this, R.layout.plain_activity)

        binding.lifecycleOwner = this

        //➁
        binding.viewmodel = viewModel
    }
}

➀はbinding先のレイアウト(plain_activity.xml)が定義されています。

➁はbinding.viewmodel = viewModelと定義されています。
binding.viewmodelの”viewmodel”は、plain_activity.xmlで定義したviewodelのことを指しています。

一方、右辺のviewModel(※Mが大文字なので”viewmodel”とは異なる)はSimpleViewModel.ktのViewModelを、 ViewModelProvidersで受け取ったものです。

なので、binding.viewmodel = viewModelは
plain_activity.xmlとSimpleViewModel.ktのViewModelの紐付けを表しています。

ここまでの関係図を矢印で書くと、こんな感じになります。↓
主要クラス構成図(矢印追加後)png.png

plain_activity.xmlとSimpleViewModel.ktが紐づけされた状態なので、
android:text="@{viewmodel.name}"や、app:hideIfZero="@{viewmodel.likes}"**のように、レイアウトからSimpleViewModel.ktの変数(name, likes)を引用することができます。

例えばnameは、_nameが代入されており、_nameはMutableLiveData("Ada")として定義されているので、Nameのところに”Ada”が表示されます。
アプリ画面(Nameに赤枠付き).png

BindingAdapter

さて、残りのBindingAdapter.ktでは、plain_activity.xmlが受け取った変数を引数とするメソッドが定義されています。以下に一例を挙げます。

BindingAdapter.kt
@BindingAdapter("app:popularityIcon")
fun popularityIcon(view: ImageView, popularity: Popularity) {

    val color = getAssociatedColor(popularity, view.context)

    ImageViewCompat.setImageTintList(view, ColorStateList.valueOf(color))

    view.setImageDrawable(getDrawablePopularity(popularity, view.context))
}
plain_activity.xml
<ImageView
app:popularityIcon="@{viewmodel.popularity}"
以下省略
/>

この場合だと@BindingAdapter(app:popularityIcon)という表記で、popularityIconメソッドが、plain_activity.xmlのImageViewと紐付けされています。

メソッドの引数の型は、(ビューの型,レイアウト側から受けとる変数の型)となっています。
上の例だと、fun popularityIcon (view: ImageView, popularity: Popularity)のようになっていて、渡されたpopularityの中身によってメソッドの処理結果が変わるため、ImageViewの画像表示が切り替わるようになっています。

これで、SimpleViewModel.ktからBindingAdapter.ktまでのつながりが(ふわ~っと笑)確認できました。
主要クラス構成図(レイアウトとBindingAdapterの紐づけ 全体図)png.png

BindingAdapterの部分だけ抜き出した場合はこんな感じ↓
主要クラス構成図(レイアウトとBindingAdapterの紐づけ)png.png

ボタンタップからUI更新の流れ

このアプリはLIKEボタンがトリガーになっていて、LIKEボタンのタップ回数によって画像や色が変わっていきますが、どういう挙動になっているのか確認するために、流れをまとめました。
(図はなんやかんや省略してます。"イメージをつかむ"程度の認識で眺めてください)

LIKEボタンのタップ

・LIKEボタンを押すと、ボタンがSimpleViewModelのonLike()メソッドと紐付いているので、_likesが増える。

・_likesはlikes、popularityの二つのLiveDataに代入されるので、2つに分岐する。

LIKEボタン押したときの分岐.png

likes側

➀TextViewがlikesを受け取り、String型に変換し、タップ数として表示する。

➁プログレスバー(ProgressBar)は2つの@BindingAdapterと紐づいているので分岐する。
 ➁-1 likesが0ならばプログレスバーを非表示、それ以外の場合は表示する。
 ➁-2 likesの数量に応じてプログレスバーの進捗具合を表示する。

LIKEボタン押したときの分岐(変数_likes).png

popularity側

Transformations.mapで_likesがPopularity型へ変換され、when式でPopularityレベルを分ける。

➀、➁共にSimpleViewModel.ktからpopularityを受け取り、@BindingAdapterで紐づいたメソッドへ、引数として渡す。
➀はImageViewの画像の切り替え、➁はプログレスバーの色変更をする。

LIKEボタン押したときの分岐(popularity).png

感想

まとめるのにだいぶ時間がかかってしまいましたが、概略図がなんとなくイメージできるようになったので、やった意味はあったと思います。実際にたくさん使って、何か作ってみようと思います~^^

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1