RecyclerView 适配器 notifyDataSetChanged 停止花哨的动画
Posted
技术标签:
【中文标题】RecyclerView 适配器 notifyDataSetChanged 停止花哨的动画【英文标题】:RecyclerView Adapter notifyDataSetChanged stops fancy animation 【发布时间】:2015-02-02 17:35:44 【问题描述】:我正在构建一个基于 RecyclerView 的组件,允许用户通过拖放重新排序项目。 一旦我在 DragListener 端,我需要它在适配器中的位置才能执行正确的移动,但我只能访问视图。 所以这就是我在适配器视图绑定中所做的:
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position)
Track track = mArray.get(position);
viewHolder.itemView.setTag(R.string.TAG_ITEM_POSITION, position);
你觉得它正确吗? 因为如果我像这样移动一个项目:
public void move(int from, int to)
Track track = mArray.remove(from);
mArray.add(to, track);
notifyItemMoved(from, to);
那么位置标记不再正确,如果我 notifyDataSetChanged(),我会失去花哨的动画。 有什么建议吗?
【问题讨论】:
【参考方案1】:有一种方法可以只使用notifyDataSetChanged()
来保存精美的动画
您需要创建自己的GridLayoutManager
,并覆盖supportsPredictiveItemAnimations()
方法返回true
;
您需要mAdapter.setHasStableIds(true)
我觉得棘手的部分是您需要覆盖适配器的 getItemId()
方法。它应该返回真正唯一的值,而不是position
的直接函数。类似mItems.get(position).hashCode()
在我的情况下工作得非常好 - 仅使用 notifyDataSetChanged()
来添加、删除和移动项目的漂亮动画
【讨论】:
你救了我很多痛苦,tyvm。顺便说一句,这应该被接受,而不是上面的答案。 我忘了说,如果您使用 DB 来为适配器提供数据,那么第 3 部分的主键之类的东西将解决您的问题。 嗨,我不明白你需要什么使用“mItems.get(position).hashCode()”返回 long 我需要返回一个对象 ..,你能告诉我吗?以及如何返回对象?谢谢 这是为您的 getItemId (int position) 方法返回 long。 No 3 让我的应用开心了!! :) 它无法控制自己的情绪..【参考方案2】:不,这是错误的。首先,您不能引用该方法返回后传递给 onBindViewHolder 的位置。 RecyclerView 不会在其位置发生变化时重新绑定视图(由于项目移动等)。
相反,您可以使用ViewHolder#getPosition()
,它将返回更新后的位置。
如果你解决了这个问题,你的移动代码应该可以工作并提供漂亮的动画。
调用notifyDataSetChanged
将阻止预测动画,因此请尽可能避免。详情请见documentation。
编辑(来自评论):从外部获取位置,从 recyclerview 获取子视图持有者,然后从 vh 获取位置。详见 RecyclerView api
【讨论】:
好的,谢谢您的回答 =) 但是现在从 adpater 外部检索位置的最佳方法是什么(例如我的 dragListener)? 从 recyclerview 获取子视图持有者,然后从 vh 获取位置。详见 RecyclerView api getPosition() 已被弃用,因为它太模棱两可了。在这种情况下,您需要使用 getLayoutPosition() 但是mAdapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
很烂。我看到项目被移动了Collections.swap(....,...)
但是当我点击它们时它们是旧的(所以只有项目的标题被改变了)【参考方案3】:
1) 您将使用notifyItemInserted(position);
或notifyItemRemoved(position);
而不是notifyDataSetChanged()
来制作动画。
2)您可以手动解决您的问题 - 使用
public void move(int from, int to)
Track track = mArray.remove(from);
mArray.add(to, track);
notifyItemMoved(from, to);
ViewHolder fromHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(from);
ViewHolder toHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(to);
Tag fromTag = fromHolder.itemView.getTag();
fromHolder.itemView.setTag(toHolder.itemView.getTag());
toHolder.itemView.setTag(fromTag);
【讨论】:
1. notifyItemChanged(位置)【参考方案4】:您应该将您的方法移至OnCreateViewHolder
,然后notifyItemRemoved(index)
才能正常工作。
【讨论】:
【参考方案5】:我可以通过将其添加到列表项的外部元素来维持触摸动画
<View
android:foreground="?android:attr/selectableItemBackground"
...>
【讨论】:
【参考方案6】:我使用 'notifyItemChanged(int position);' 修复了它而不是 'notifyDataSetChanged();'
我的适配器完美显示精美的动画,没有任何延迟
编辑:我从 onBindViewHolder 的位置得到位置。
【讨论】:
【参考方案7】:正如上面其他人所说,您可以在适配器上使用 notifyDataSetChanged
时制作动画,尽管您需要专门使用稳定的 ID。如果您的项目 ID 是字符串,您可以为您拥有的每个字符串 ID 生成一个长 ID,并将它们保存在地图中。例如:
class StringToLongIdMap
private var stringToLongMap = HashMap<String, Long>()
private var longId: Long = 0
fun getLongId(stringId: String): Long
if (!stringToLongMap.containsKey(stringId))
stringToLongMap[stringId] = longId++
return stringToLongMap[stringId] ?: -1
然后在你的适配器中:
private var stringToLongIdMap = StringToLongIdMap()
override fun getItemId(position: Int): Long
val item = items[position]
return stringToLongIdMap.getLongId(item.id)
另一个需要考虑的有用的事情是,如果您使用 kotlin 数据类作为适配器中的项目,并且您没有 id,则可以使用数据类本身的 hashCode 作为稳定 id(如果您确定项目属性组合在您的数据集中是唯一的):
override fun getItemId(position: Int): Long = items[position].hashCode().toLong()
【讨论】:
以上是关于RecyclerView 适配器 notifyDataSetChanged 停止花哨的动画的主要内容,如果未能解决你的问题,请参考以下文章
Fragment,RecyclerView:没有附加适配器;跳过布局
片段中的recyclerview的“RecyclerView:没有附加适配器;跳过布局”[重复]
RecyclerView 适配器中的 onBackPressed()
当调用 RecyclerView 适配器的“notifyDataSetChanged()”时,RecyclerView 抛出“java.lang.Throwable: addInArray”