如何更新 RecyclerView 项目内的绑定?
How to update the bindings inside a RecyclerView item?
想象一个 Whosebug 问题的列表,这些问题可以单独被赞成和反对。他们每个人都有一个计数,显示其投票数。单击赞成票会使计数增加一,单击反对票会使计数减少一。这就是我想要实现的总体思路。 RecyclerView 中单个项目的布局,rv_item.xml
(为简洁起见进行了压缩):
data>
<variable
name="item"
type="com.mycodez.Item"/>
<variable
name="vm"
type="com.mycodez.ListViewModel" />
</data>
<Button
android:id="@+id/decrease_quantity"
android:onClick="@{() -> vm.decrementItemQuantity(item)}"/>
<TextView
android:id="@+id/quantity"
android:text="@{String.valueOf(item.quantity)}"
tools:text="1" />
<Button
android:id="@+id/increase_quantity"
android:onClick="@{() -> vm.incrementItemQuantity(item)}"/>
其中 @+id/decrease_quantity
类似于反对票,@+id/quantity
类似于投票计数,@+id/increase_quantity
类似于赞成票。适配器:
class ListRvAdapter extends RecyclerView.Adapter<ListRvAdapter.ViewHolder> {
private List<Item> items;
private ListViewModel viewModel;
private LifecycleOwner lifecycleOwner;
ListRvAdapter(List<Item> items, ListViewModel viewModel, LifecycleOwner lifecycleOwner) {
this.items = items;
this.viewModel = viewModel;
this.lifecycleOwner = lifecycleOwner;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
RvItemBinding binding = RvItemBinding.inflate(layoutInflater, parent, false);
return new ViewHolder(binding, viewModel, lifecycleOwner);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final RvItemBinding binding;
private ListViewModel viewModel;
private LifecycleOwner lifecycleOwner;
ViewHolder(RvItemBinding binding, ListViewModel viewModel, LifecycleOwner lifecycleOwner) {
super(binding.getRoot());
this.binding = binding;
this.viewModel = viewModel;
this.lifecycleOwner = lifecycleOwner;
}
public void bind(Item item) {
binding.setItem(item);
binding.setVm(viewModel);
binding.executePendingBindings();
binding.setLifecycleOwner(lifecycleOwner);
}
}
}
我省略了 ViewModel,ListViewModel
因为 vm.decrementItemQuantity(item)
和 vm.incrementItemQuantity(item)
本质上做的是调用模型来更新数据库中的数量:
@Query("UPDATE items SET quantity=quantity + 1 WHERE id=:itemId")
Completable incrementQuantity(int itemId);
@Query("UPDATE items SET quantity=quantity - 1 WHERE quantity > 1 AND id=:itemId")
Completable decrementQuantity(int itemId);
因此,当模型中的数量正在更新时,我不清楚我应该如何更新该特定 RecyclerView 项目的 UI 以反映该更改。通常,当使用 LiveData 和 DataBinding 时,我会做类似 itemQuantityLiveData.setValue(item.quantity)
的事情,这会立即反映在 XML 中。但在这种情况下,我在我的 ViewHolder 中使用 bind(Item item)
方法,并且 item
对象作为变量而不是表达式传递给 DataBinding 布局。
我该如何解决这个问题?
不必想太多。在适配器实例上调用 notifyItemChanged(position)
是缺失的部分。因此,如何将 RecyclerView 项的索引传递回适配器就变成了一个问题。
编辑 rv_item.xml
以获取一个附加变量,position
:
<data>
<variable
name="item"
type="com.mycodez.Item"/>
<!-- add this variable -->
<variable
name="position"
type="int"/>
<variable
name="vm"
type="com.mycodez.ListViewModel" />
</data>
此外,在同一个 XML 文件中,将 position
传递给 onClick
触发的 vm
方法:
<Button
android:id="@+id/decrease_quantity"
android:onClick="@{() -> vm.decrementItemQuantity(item, position)}"/>
<Button
android:id="@+id/increase_quantity"
android:onClick="@{() -> vm.incrementItemQuantity(item, position)}"/>
那么ListRvAdapter
ViewHolder
中的bind
方法就变成了:
public void bind(Item item, int position) { // new argument
binding.setItem(item);
binding.setPosition(position); // pass position to the layout
binding.setVm(viewModel);
binding.executePendingBindings();
binding.setLifecycleOwner(lifecycleOwner);
}
像这样在适配器的 onBindViewHolder
中传递新参数:
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position), position);
}
最后在ListRvAdapter
中增加两个方法分别增加和减少数量:
public void increaseQuantity(int position) {
Item item = items.get(position);
item.quantity += 1;
notifyItemChanged(position);
}
public void decreaseQuantity(int position) {
Item item = items.get(position);
item.quantity -= 1;
notifyItemChanged(position);
}
所有这些都准备就绪后,单击按钮 @+id/increase_quantity
时,将触发 vm.incrementItemQuantity(item, position)
。在 MVVM 设置中,ViewModel 将具有 position
:
的 LiveData 事件对象
public MutableLiveData<Event<Integer>> itemQuantityIncreased = new MutableLiveData<>();
并且对 vm.incrementItemQuantity(item, position)
的调用将设置该对象的值:
itemQuantityIncreased.setValue(new Event<>(position))
;
最后,View 将在 ViewModel 的 itemQuantityIncreased
LiveData 对象上有一个 Observer。因为 Event 会发送 RecyclerView 项目的位置,View 可以掌握它并在适当的地方调用 adapter.increaseQuantity(position)
。 adapter.decreaseQuantity(position)
效果一样。
想象一个 Whosebug 问题的列表,这些问题可以单独被赞成和反对。他们每个人都有一个计数,显示其投票数。单击赞成票会使计数增加一,单击反对票会使计数减少一。这就是我想要实现的总体思路。 RecyclerView 中单个项目的布局,rv_item.xml
(为简洁起见进行了压缩):
data>
<variable
name="item"
type="com.mycodez.Item"/>
<variable
name="vm"
type="com.mycodez.ListViewModel" />
</data>
<Button
android:id="@+id/decrease_quantity"
android:onClick="@{() -> vm.decrementItemQuantity(item)}"/>
<TextView
android:id="@+id/quantity"
android:text="@{String.valueOf(item.quantity)}"
tools:text="1" />
<Button
android:id="@+id/increase_quantity"
android:onClick="@{() -> vm.incrementItemQuantity(item)}"/>
其中 @+id/decrease_quantity
类似于反对票,@+id/quantity
类似于投票计数,@+id/increase_quantity
类似于赞成票。适配器:
class ListRvAdapter extends RecyclerView.Adapter<ListRvAdapter.ViewHolder> {
private List<Item> items;
private ListViewModel viewModel;
private LifecycleOwner lifecycleOwner;
ListRvAdapter(List<Item> items, ListViewModel viewModel, LifecycleOwner lifecycleOwner) {
this.items = items;
this.viewModel = viewModel;
this.lifecycleOwner = lifecycleOwner;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
RvItemBinding binding = RvItemBinding.inflate(layoutInflater, parent, false);
return new ViewHolder(binding, viewModel, lifecycleOwner);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final RvItemBinding binding;
private ListViewModel viewModel;
private LifecycleOwner lifecycleOwner;
ViewHolder(RvItemBinding binding, ListViewModel viewModel, LifecycleOwner lifecycleOwner) {
super(binding.getRoot());
this.binding = binding;
this.viewModel = viewModel;
this.lifecycleOwner = lifecycleOwner;
}
public void bind(Item item) {
binding.setItem(item);
binding.setVm(viewModel);
binding.executePendingBindings();
binding.setLifecycleOwner(lifecycleOwner);
}
}
}
我省略了 ViewModel,ListViewModel
因为 vm.decrementItemQuantity(item)
和 vm.incrementItemQuantity(item)
本质上做的是调用模型来更新数据库中的数量:
@Query("UPDATE items SET quantity=quantity + 1 WHERE id=:itemId")
Completable incrementQuantity(int itemId);
@Query("UPDATE items SET quantity=quantity - 1 WHERE quantity > 1 AND id=:itemId")
Completable decrementQuantity(int itemId);
因此,当模型中的数量正在更新时,我不清楚我应该如何更新该特定 RecyclerView 项目的 UI 以反映该更改。通常,当使用 LiveData 和 DataBinding 时,我会做类似 itemQuantityLiveData.setValue(item.quantity)
的事情,这会立即反映在 XML 中。但在这种情况下,我在我的 ViewHolder 中使用 bind(Item item)
方法,并且 item
对象作为变量而不是表达式传递给 DataBinding 布局。
我该如何解决这个问题?
不必想太多。在适配器实例上调用 notifyItemChanged(position)
是缺失的部分。因此,如何将 RecyclerView 项的索引传递回适配器就变成了一个问题。
编辑 rv_item.xml
以获取一个附加变量,position
:
<data>
<variable
name="item"
type="com.mycodez.Item"/>
<!-- add this variable -->
<variable
name="position"
type="int"/>
<variable
name="vm"
type="com.mycodez.ListViewModel" />
</data>
此外,在同一个 XML 文件中,将 position
传递给 onClick
触发的 vm
方法:
<Button
android:id="@+id/decrease_quantity"
android:onClick="@{() -> vm.decrementItemQuantity(item, position)}"/>
<Button
android:id="@+id/increase_quantity"
android:onClick="@{() -> vm.incrementItemQuantity(item, position)}"/>
那么ListRvAdapter
ViewHolder
中的bind
方法就变成了:
public void bind(Item item, int position) { // new argument
binding.setItem(item);
binding.setPosition(position); // pass position to the layout
binding.setVm(viewModel);
binding.executePendingBindings();
binding.setLifecycleOwner(lifecycleOwner);
}
像这样在适配器的 onBindViewHolder
中传递新参数:
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position), position);
}
最后在ListRvAdapter
中增加两个方法分别增加和减少数量:
public void increaseQuantity(int position) {
Item item = items.get(position);
item.quantity += 1;
notifyItemChanged(position);
}
public void decreaseQuantity(int position) {
Item item = items.get(position);
item.quantity -= 1;
notifyItemChanged(position);
}
所有这些都准备就绪后,单击按钮 @+id/increase_quantity
时,将触发 vm.incrementItemQuantity(item, position)
。在 MVVM 设置中,ViewModel 将具有 position
:
public MutableLiveData<Event<Integer>> itemQuantityIncreased = new MutableLiveData<>();
并且对 vm.incrementItemQuantity(item, position)
的调用将设置该对象的值:
itemQuantityIncreased.setValue(new Event<>(position))
;
最后,View 将在 ViewModel 的 itemQuantityIncreased
LiveData 对象上有一个 Observer。因为 Event 会发送 RecyclerView 项目的位置,View 可以掌握它并在适当的地方调用 adapter.increaseQuantity(position)
。 adapter.decreaseQuantity(position)
效果一样。