项目 ViewModel 的 RecyclerView 数据绑定问题

RecyclerView data binding issue with item ViewModel

在我的代码中,我使用了两种视图模型:一种用于 RecyclerView 中使用的项目列表,另一种用于项目本身,我将其绑定到项目详细信息视图。 创建视图后,我在从项目详细信息视图模型到视图进行任何更新时遇到问题。因此在下面的代码中,当单击按钮时,会调用 ItemViewModel 中的 onButtonClick() 函数,但 item_card 视图中的文本更新不会更新。

非常感谢任何想法。

谢谢!

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var itemListVM: ItemListViewModel

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

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this

        setupBindings()
    }

private fun setupBindings() {

    binding.lifecycleOwner = this

    // Create VM
    itemListVM = ViewModelProviders.of(this).get(ItemListViewModel::class.java)
    binding.itemListVM = itemListVM

    setupRecyclerView()

}

// Set up RecyclerView
private fun setupRecyclerView() {
    val recyclerView = binding.itemListRecyclerview
    val layoutManager = LinearLayoutManager(this)
    recyclerView.layoutManager = layoutManager

     // Set adapter
    recyclerView.adapter = itemListVM.getAdapter()

    // Create some data
    var itemList = mutableListOf<ItemModel>()
    for (i in 0..5){
        var item = ItemModel()
        itemList.add(item)
    }

    itemListVM.setItemList(itemList)
}

ItemModel.kt:
class ItemModel{
   var text = ""
}

 ItemViewModel.kt:
 class ItemViewModel: ViewModel(){
    var text = MutableLiveData<String>()
    lateinit var itemModel : ItemModel
    var position : Int = -1

    init{
        text.value = "init"
    }

    fun setItem(itemModel: ItemModel){
        this.itemModel = itemModel
        text.value = "set model"
    }

    fun onButtonClick(){
        Log.d("ItemViewModel", "position: $position")
        text.value = "button click"
    }
}

ItemListViewModel.kt:
class ItemListViewModel: ViewModel(){

    var itemVMMap = mutableMapOf<Int, ItemViewModel>()
    var itemListAdapter : ItemListAdapter? = null

    init {
        itemListAdapter = ItemListAdapter(this)
    }

    fun getAdapter(): ItemListAdapter? {
        return itemListAdapter
    }

    fun setItemList(itemList: List<ItemModel>){
        itemListAdapter?.setItemList(itemList)
    }

    fun getItemAt(position: Int?): ItemModel? {
        val itemList = itemListAdapter?.getItemList()

        if (itemList != null &&
            position != null &&
            itemList.size > position){
            return itemList.get(position)
        }
        else {
            return null
        }
    }

    fun getItemVMAtPosition(position: Int?):ItemViewModel?{
        // Check if VM exists
        var itemVM: ItemViewModel? = null
        if (itemVMMap.contains(position)){
            itemVM = itemVMMap[position]
        }
        else {
            // Create a new VM
            itemVM = ItemViewModel()
            itemVM.position = position!!

            // Get item
            val item = getItemAt(position)

            item?.let{
                itemVM?.setItem(it)
            }

            itemVMMap[position!!] = itemVM
        }

        return itemVM
    }
}

ItemListAdapter.kt:
class ItemListAdapter
internal constructor(listViewModel : ItemListViewModel) : RecyclerView.Adapter<ItemListAdapter.GenericViewHolder>() {

    private var itemList: List<ItemModel>? = null
    private var itemListVM: ItemListViewModel? = null
    private var layoutId: Int = 0

    init {
        this.layoutId = layoutId
        this.itemListVM = listViewModel
    }

    private fun getLayoutIdForPosition(position: Int): Int {
        return layoutId
    }

    override fun getItemCount(): Int {
        return itemList?.size ?: 0
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(
            layoutInflater,
            com.example.test2.R.layout.item_card,
            parent,
            false
        ) as com.example.test2.databinding.ItemCardBinding

        return GenericViewHolder(binding)
    }

    override fun onBindViewHolder(holder: GenericViewHolder, position: Int) {
        if (itemListVM != null) {
            val itemVM = itemListVM!!.getItemVMAtPosition(position)
            holder.bind(itemVM!!, position)
        }
    }

    override fun getItemViewType(position: Int): Int {
        return getLayoutIdForPosition(position)
    }

    // View Holder Definition
    inner class GenericViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bind(viewModel: ItemViewModel, position: Int?) {
            binding.setVariable(BR.itemListVM, itemListVM)
            binding.setVariable(BR.position, position)
            binding.setVariable(BR.itemVM, viewModel)

            binding.executePendingBindings()
        }
    }

    // Setters
    fun setItemList(itemList: List<ItemModel>?) {
        this.itemList = itemList
        notifyDataSetChanged()
    }

    // Getters
    fun getItemList(): List<ItemModel>? {
        return itemList
    }
}

activity_main.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="itemListVM" type="com.example.test2.ItemListViewModel"/>
    </data>
    <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">

            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/item_list_recyclerview"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:scrollbars="vertical">
            </androidx.recyclerview.widget.RecyclerView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

item_card.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">
    <data>
        <import type="android.view.View" />
        <variable name="itemListVM" type="com.example.test2.ItemListViewModel"/>
        <variable name="position" type="java.lang.Integer" />
        <variable name="itemVM" type="com.example.test2.ItemViewModel"/>
    </data>
    <LinearLayout
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                    android:orientation="horizontal"
                    android:layout_margin="12dp">
        <TextView android:layout_width="wrap_content"
                  android:layout_height="wrap_content"
                  android:text="@{itemVM.text}"/>
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:paddingLeft="12dp"
                android:onClick="@{() -> itemVM.onButtonClick()}"
                android:text="Click Me"/>
    </LinearLayout>
</layout>

这只是因为您使用的是可变的 属性,但没有以某种方式通知您的视图您的值正在更新。要直接绑定它们,您应该使它成为一个可观察对象,然后在数据绑定中将其附加到视图中,或者您应该通知您对该变量所做的更新。

public class Foo {
    public final ObservableField<String> baz = new ObservableField<>();
}

但是在视图方面这样做,

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{foo.baz}"/>

在更改值时,您可以这样做 -

baz.set("changed value") 它会自动更新您的视图。