项目 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") 它会自动更新您的视图。
在我的代码中,我使用了两种视图模型:一种用于 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") 它会自动更新您的视图。