この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
今回はMVVMでRecyclerViewにアイテムを表示させる方法です。
DiffUtilとObservableArrayListを使うことで、リストが変化した際にViewに反映されるようにしました。
実装
手順は以下の通りです。
- DiffUtil.Callbackを作る
- BindingAdapterを作る
- ViewModelにObservableArrayListを持たせる
- layoutでバインドする
DiffUtil.Callback
class ResultDiffCallback(val oldItems: List<ResultDisplayItem>, val newItems: List<ResultDisplayItem>) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldItems.size
override fun getNewListSize(): Int = newItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition].itemId == newItems[newItemPosition].itemId
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition] == newItems[newItemPosition]
}
}
BindingAdapter
object RecyclerViewBindingAdapter {
@JvmStatic
@BindingAdapter("bind:searchResult")
fun showSearchResult(view: RecyclerView, items: MutableList<ResultDisplayItem>) {
val adapter = view.adapter as ResultRecyclerAdapter
val diff = DiffUtil.calculateDiff(ResultDiffCallback(adapter.mItems, items), true)
adapter.mItems = items
diff.dispatchUpdatesTo(adapter)
}
}
ViewModel
class ResultViewModel {
val items: ObservableArrayList<ResultDisplayItem> = ObservableArrayList()
fun searchExec(query: SearchQuery) {
// APIなどから取得した結果をリストにセット追加する
items.addAll(response.results)
}
}
layout
<?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"
>
<data>
<variable
name="viewModel"
type="com.classmethod.app.features.result.ResultViewModel"
/>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:searchResult="@{viewModel.items}"
/>
</FrameLayout>
</layout>
補足
DiffUtilは、リストの比較をしつつ、RecyclerViewへの反映をやってくれる便利なクラスです。
Adapterのリストは自分で更新する必要がありますが、notifyをやってくれます。(下記参照)
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
dispatchUpdatesTo(new ListUpdateCallback() {
@Override
public void onInserted(int position, int count) {
adapter.notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
adapter.notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
adapter.notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count, Object payload) {
adapter.notifyItemRangeChanged(position, count, payload);
}
});
}
まとめ
DiffUtilのおかげで、MVVM + RecyclerViewもやりやすくなったと感じます。
最下部スクロールで追加読み込みする画面などで威力を発揮するでしょう。
余談
Android Architecture ComponentsでもViewModelが追加されましたが、GoogleはMVVMを推奨しているんでしょうか・・・?