Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

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

ta_sato_kuu
文系卒エンジニア。 phpから始まりc#でwindows、そして今はandroidアプリの開発をしています。 2020/04よりVRプロフェッショナルアカデミーに通ってVR開発を学びます。 1年後にVR開発の仕事への転職を目指してます。 よろしくお願いします
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした