RecycleViewでDataBindingとLiveDataを使う
これ今はもうスタートラインレベルなのに毎回用意するのが大変なので、そろそろ一気に自動生成してくれるようにしてほしい。
これは何度も書いているので未来の私のために残す。見ながら書いて足りないものに気づいたら直す感じで
Android StudioでFragment(List)を選んで生成して使用する。
MemoとMemoItemというビューの関係を関係を作るのがこの記事がゴール。使用したkotlinは 1.3.31
、 minSdkVersionは 21
gradleでdataBindingを有効にする
android { dataBinding { enabled = true } }
まずデータクラスを定義。リストの1項目を表す。Memo.kt
data class MemoItem( val id: String, val message: String ) : Serializable
ビューモデルを定義。MemoListViewModel.kt
class MemoListViewModel : ViewModel() { val items = MutableLiveData<List<MemoItem>>() fun loadItems() { val items = arrayListOf( MemoItem("1", "aaa"), MemoItem("2", "bbb"), MemoItem("3", "ccc"), MemoItem("4", "ddd"), MemoItem("5", "eee") ) this.items.value = items } }
リサイクルビューのXMLをLayoutタグでくくり、variableを定義。fragment_memo_list.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="viewModel"type="net.arvelt.memoapp.ui.memolist.MemoListViewModel" /> </data> <androidx.recyclerview.widget.RecyclerView android:id="@+id/list" android:name="net.arvelt.memoapp.MemoListFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" app:layoutManager="LinearLayoutManager" tools:context=".ui.memolist.MemoListFragment" tools:listitem="@layout/fragment_memo_list_item" /> </layout>
リサイクルビューの1行分の項目のXMLをLayoutタグでくくり、variableを定義。fragment_memo_list_itemとする
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="viewModel" type="net.arvelt.memoapp.model.MemoItem" /> </data> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/itemId" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/text_margin" android:text="@{viewModel.id}" android:textAppearance="?attr/textAppearanceListItem" /> <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/text_margin" android:text="@{viewModel.message}" android:textAppearance="?attr/textAppearanceListItem" /> </LinearLayout> </layout>
Bindingクラスを生成させるために一度コンパイルする。アダプタークラスをデータバインディングを使用した形に置き換える。MyMemoRecyclerViewAdapter.ktとする
class MyMemoRecyclerViewAdapter( private var mValues: List<MemoItem>, private val mListener: OnListFragmentInteractionListener? ) : RecyclerView.Adapter<MyMemoRecyclerViewAdapter.ViewHolder>() { private val mOnClickListener: View.OnClickListener init { mOnClickListener = View.OnClickListener { v -> val item = v.tag as MemoItem mListener?.onListFragmentInteraction(item) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding: FragmentMemoListItemBinding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.fragment_memo_list_item, parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = mValues[position] holder.binding.viewModel = MemoItem(item.id, item.message) with(holder.binding.root) { tag = item setOnClickListener(mOnClickListener) } } fun setData(items: List<MemoItem>) { this.mValues = items } override fun getItemCount(): Int = mValues.size inner class ViewHolder(val binding: FragmentMemoListItemBinding) : RecyclerView.ViewHolder(binding.root) }
MemoListFragmentのOnCreateViewをDataBidingを使用した形に置き換える。
アダプターの登録をして、viewModelを監視する。
初期値がonResumeでロードされるようにする。
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding: FragmentMemoListBinding = DataBindingUtil.inflate( inflater, R.layout.fragment_memo_list, container, false ) binding.viewModel = ViewModelProviders.of(this).get(MemoListViewModel::class.java) binding.list.adapter = MyMemoRecyclerViewAdapter(arrayListOf(), listener) binding.viewModel!!.items.observe(this, Observer { val adapter = binding.list.adapter as MyMemoRecyclerViewAdapter? adapter?.setData(it) }) this.binding = binding return binding.root } override fun onResume() { super.onResume() this.binding.viewModel!!.loadItems() }
アダプターでクリック時の処理をしているのでActivityでクリックされたときのMemoItemが取得できる。
override fun onListFragmentInteraction(item: MemoItem?) { // 遷移など }