我正在使用带有回收器视图的列表适配器,现在数据类包含一个变量 isChecked,这用于指示用户是否选择了该变量,代码正在更新列表,因为我可以看到日志(我有出于测试目的而放置)返回当前列表始终在用户单击某个项目时更新,但由于某种原因,UI 中的更改(基于 isChecked 变量)仅在滚动回收器视图或单击其他项目时反映。我放置了一个notifyDataSetChanged来查看它是否强制列表更新并查看更新的视图是否正确并且它有效,但这破坏了使用diff util的整个目的。我的包装数据类中有一个嵌套列表,正在 diff util 中进行比较,如下所示
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<MainDataClass>() {
override fun areItemsTheSame(
oldItem: MainDataClass,
newItem: MainDataClass
): Boolean {
if (oldItem is MainDataClass.Item && newItem is MainDataClass.Item) {
return oldItem.data.id == newItem.data.id
} else if (oldItem is MainDataClass.List && newItem is MainDataClass.List) {
return oldItem.list == newItem.list
} else return false
}
override fun areContentsTheSame(
oldItem: MainDataClass,
newItem: MainDataClass
): Boolean {
if (oldItem is MainDataClass.Item && newItem is MainDataClass.Item) {
return oldItem.data == newItem.data
} else if (oldItem is MainDataClass.List && newItem is MainDataClass.List) {
return oldItem.list == newItem.list
} else return false
}
}
Run Code Online (Sandbox Code Playgroud)
MainDataClass.List 包含上述特定项目的列表。
public class Item{
private Integer count;
private Integer id;
private String icon_img;
private String name;
private String cover_img;
private String group_name;
private Integer parent_id;
private Integer status;
private boolean checked = false;
private Integer whatToVisible;
public Item(Integer count, Integer id, String icon_img, String cover_img, String group_name, Integer parent_id) {
this.count = count;
this.id = id;
this.icon_img = icon_img;
this.cover_img = cover_img;
this.group_name = group_name;
this.parent_id = parent_id;
this.checked = false;
}
public Item(Integer id, String icon_img, String group_name, Integer parent_id, boolean checked) {
this.id = id;
this.icon_img = icon_img;
this.name = name;
this.group_name = group_name;
this.parent_id = parent_id;
this.checked = checked;
}
public Item(Integer id,Integer parent_id) {
this.id = id;
this.parent_id = parent_id;
}
public static Item objectExample() {
return new TrendingGroupsResponse(-2, -2);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getCount() {
return count;
}
public String getIcon_img() {
return icon_img;
}
public void setIcon_img(String icon_img) {
this.icon_img = icon_img;
}
public String getCover_img() {
return cover_img;
}
@Override
public int hashCode() {
return this.id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Item)) return false;
Item that = (Item) o;
return checked == that.checked && Objects.equals(count, that.count) && Objects.equals(id, that.id) && Objects.equals(icon_img, that.icon_img) && Objects.equals(name, that.name) && Objects.equals(cover_img, that.cover_img) && Objects.equals(group_name, that.group_name) && Objects.equals(parent_id, that.parent_id) && Objects.equals(status, that.status) && Objects.equals(whatToVisible, that.whatToVisible);
}
@Override
public String toString() {
return "id: " + this.id;
}
public void setCover_img(String cover_img) {
this.cover_img = cover_img;
}
public String getGroup_name() {
if (name != null){
group_name = name;
}
return group_name;
}
public void setGroup_name(String group_name) {
this.group_name = group_name;
}
public Integer getParent_id() {
return parent_id;
}
public void setParent_id(Integer parent_id) {
this.parent_id = parent_id;
}
public void setCount(Integer count) {
this.count = count;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getIconImg() {
return icon_img;
}
public void setIconImg(String icon_img) {
this.cover_img = icon_img;
}
public String getCoverImg() {
return cover_img;
}
public void setCoverImg(String cover_img) {
this.cover_img = cover_img;
}
public String getGroupName() {
if (name != null) {
group_name = name;
}
return group_name;
}
public void setGroupName(String group_name) {
this.group_name = group_name;
}
public Integer getWhatToVisible() {
return whatToVisible;
}
public void setWhatToVisible(Integer whatToVisible) {
this.whatToVisible = whatToVisible;
}
Run Code Online (Sandbox Code Playgroud)
从主列表中单击主项目时调用的方法
fun addOrRemoveSelectedItemsIfPresent(item: Item) {
viewModelScope.launch {
addOrRemoveItemsFromPopularItems(item.id.toString())
updateAllItems(item)
var itemList = _selectedItems.value
if (itemList == null) itemList = ArrayList()
itemList.forEach { item1: Item ->
if (item1.id == item.id) {
itemList.remove(group)
selectedItemCount.set(selectedItemCount.get() - 1)
_selectedItems.value = itemList
return@launch
}
}
itemList.add(response)
selectedItemCount.set(selectedItemCount.get() + 1)
_selectedItems.value = itemList
}
}
private fun addOrRemoveItemsFromPopularItems(id: String) {
val popularItems = _popularItemsLiveData.value?.data
popularItems?.forEach {
if (it.id.toString() == id) {
if (it.isChecked == null || it.isChecked == false) {
it.isChecked = true
} else {
it.isChecked = false
}
}
}
_popularItemsLiveData.postValue(Success(popularItems))
}
Run Code Online (Sandbox Code Playgroud)
让我们在这里分解一下这个问题:
List<Things>(我们称它们为“事物”以保护其身份)。allList。allList。我们称之为selectedList. (嘿,我并没有说我擅长命名事物)。现在的关键是当您点击 中的项目时会发生什么allList。selectedList如果该项目不存在,您希望将其添加到列表中;或者如果已选择该项目,则将其从列表中删除。
这就是你需要发生的事情。
我将这样做:
selectedList)。虚拟机并不真正关心该列表中的内容,这不是它的工作。这就是用例/委托。但是让我们暂时忽略抽象的好处,并假设虚拟机有一个可以为您做到这一点的方法:fun updateSelectedList(selectedItem: Thing): List<Thing> //remember we call 'em "things".
该方法内部发生的情况尚不相关。
然后,您通过 liveData 将此列表传递到您的 Fragment/UI,它将submitList(...)。这一切都有效。您还在isChecked某处更新了项目的状态,因此allList也可以更新其“视图状态”以选中或取消选中视图。
在我们讨论潜在问题之前,假设您有一个完美的代码库,其中返回了updateSelectedList您提交到 recyclerView 的列表,并且它工作正常(与您allList现在声称的工作正常一样)。
在这个完美的场景中,DiffUtil 不会发现自己遇到任何麻烦,如果一个项目是新的(或消失),它将能够计算差异并每次在正确的位置呈现适当的视图,就像它经常做的那样。
您声称在调用 DiffUtil 时, 的值isChecked已经更改,因此 diffUtil 不会检测到差异。
这让我相信,也是我写这个冗长答案的原因,您传递给每个适配器的数据是相同的,因此当您修改 to selectedListbe中的项目时isSelected = true/false,您实际上是在修改相同的引用/包含的对象allList。
为了更好地说明这一点,我制作了一个 Kotlin Playground,并用一个示例来说明我的意思,可以在 play.kotlinlang.org 中找到它
我相信您执行步骤的顺序会导致数据发生变异,当 RecyclerView 需要计算更改时,它们已经发生了。
我看待这样的问题的方式是:
换句话说。
您选择(或取消选择)“所有列表”中的一个项目,您的虚拟机评估需要执行的操作,并生成一个新的不可变列表并复制该项目。
您链接的答案建议使用toList()它来创建新列表,但这是浅复制,而不是深复制,因此项目仍然相同,改变的是实际列表。意义:
val item1 = "A"
val item2 = "B"
val list1 = mutableList(item1, item2)
val list2 = list1.toList()
Run Code Online (Sandbox Code Playgroud)
其中 ^^^ list2[0] 和 list2[1] 分别指向与 list1[0] 和 [1] 相同的对象。如果更改 item1,则会在两个列表中更改它。
我怀疑这就是发生在你身上的事情。
再说一次,不要只做一个toList。从最好是不可变的对象构造一个新的不可变列表(因此使用data class并使用 val,而不是 var)。
最好这样做:
val modifiedItem = oldItem.copy(isSelected = true)
我认为所有这些数据转换(修改两个列表和项目)应该在 ViewModel 中几乎同时发生(如果需要,可以委托给用例)。
我希望这能给你一个有用的方向推动。
请记住:关注点分离是你的朋友。在您的完美世界中,适配器的工作是计算列表 A 和列表 B 之间的差异并渲染它们。而已。
在选定的列表中,所发生的只是项目出现并重新出现(基于它们是否被选择)。该列表应该简单地由所选项目组成(不需要是副本,只要您不以任何方式改变它们,只需显示它们)。
在all列表中,项目大多保持在相同的位置,但它们的内容发生变化(以指示它们是否被选择)。同样,这是列表接收的只读对象的不可变列表。
想想这个场景:
您的all list包含 2 件物品。
两者均未选择。
您选择项目“A”(第一个)。如果您做对了,您收到的下一个列表是:
item[0](第一项)是isSelected = true。这将与之前 isSelected 为 false 的列表进行比较。它应该有效。几年前我不得不做一件非常类似的事情;)
| 归档时间: |
|
| 查看次数: |
2759 次 |
| 最近记录: |