Edited at

RecyclerView + Databinding + ViewModel + LiveData で RecyclerViewのitemの値を変更する


はじめに

recyclerViewのitemの要素をliveDataで管理したくて色々試した結果です。

今回はrecyclerViewのitemのテキストをボタンをクリックしたら変わるようにしてみたいと思います。

僕もあまり理解が深いわけではないので、間違っているところがあったらご指摘していただけると嬉しいです。

android studioのバージョンは以下です。

android Studio : 3.4.1


実装


build.gradleはこんな感じ(追加したのだけ)


build.gradle

android {

// ・・・
dataBinding {
enabled = true
}
}

dependencies {
// ・・・
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation "android.arch.lifecycle:extensions:1.1.1"
// ・・・
}


databindingをtrueにしてlifecycle:extensionを入れただけです。


xml(constraintLayoutの制約は省略)


item_recycler_view.xml

<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="MyViewModel"/>
<variable name="position" type="Integer"/>
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:text="@{viewModel.list[position]}"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:id="@+id/textView" />

<View
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@android:color/background_dark"/>

</androidx.constraintlayout.widget.ConstraintLayout>

</layout>


main_activityのxmlは省略しますがボタンとrecyclerViewがおいてあるだけです。


続いてviewModel


MyViewModel.kt

class MyViewModel : ViewModel() {

val list = mutableListOf<MutableLiveData<String>>()
init {
for (i in 0..3) {
list.add(MutableLiveData<String>().apply { value = "Item$i" })
}
}

fun buttonClick(view: View) {
list[1].value = "Success!!"
}
}


ボタンをクリックしたらrecyclerViewで使っているlistの中の一つをSuccess!!に変えてみます。

まあ普通にliveDataを更新しているだけです。


続いてMainActivity


MainActivity.kt

class MainActivity : AppCompatActivity() {

lateinit var mViewModel: MyViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewModel = ViewModelProviders.of(this).get(MyViewModel().javaClass)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewModel = mViewModel
binding.lifecycleOwner = this
//ここまではよくあるdataBindingと同じです

//ここでadapterを作成するときにlifecycleOwnerを渡す!
val adapter = MyAdapter(mViewModel, this)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
}
}


adapterにactivityのlifecycleOwnerを渡しました。


AdapterとViewHolder


MyAdapter.kt

//引数としてrecyclerViewを使っているactivity,fragmentのlifecycleOwnerをもらいます

class MyAdapter(private val viewModel: MyViewModel, private val parentLifecycleOwner: LifecycleOwner) :
RecyclerView.Adapter<MyAdapter.MyHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val binding = DataBindingUtil.inflate<ItemRecyclerViewBinding>(
LayoutInflater.from(parent.context),
R.layout.item_recycler_view,
parent,
false
)
return MyHolder(binding)
}

override fun getItemCount(): Int {
return viewModel.list.size
}

override fun onBindViewHolder(holder: MyHolder, position: Int) {
holder.binding.viewModel = viewModel
holder.binding.position = position
//ここでviewholderのlifecycleOwnerにセットする!
holder.binding.lifecycleOwner = parentLifecycleOwner
}

class MyHolder(val binding: ItemRecyclerViewBinding) : RecyclerView.ViewHolder(binding.root)
}


先ほどactivityからもらったlifecycleOwnerを

holder.binding.lifecycleOwner = parentLifecycleOwner

という感じでセットしてあげます。

やることは以上!

これでrecyclerViewの中のitemもliveDataの変更が反映されるようになります。

activityやfragmentでは

binding.lifecycleOwner = lifecycleOwnerという感じで書きますよね。

ただそれをadapterの方で生成したbindingでもしてあげるだけでした。


おわりに

結果的にはとても簡単なことでしたが、気づくまでに時間がかかってしまいました。

もっとandroidについて学習しなきゃと思わされた出来事でした。

またこの記事ではlifecycleOwnerにthisを使っていますが、fragmentでやる場合はviewLifecycleOwnerを使った方がいいらしいです。以下の記事が参考になるのでお時間のある方はどうぞ

5 common mistakes when using Architecture Components

何か間違いやさらに良い方法があればコメントで教えていただけると嬉しいです。

ありがとうございました。