RecyclerView适配器notifyDataSetChanged停止花哨的动画

时间:2014-12-04 17:58:54

标签: android adapter android-recyclerview

我正在构建一个基于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(),我会失去花哨的动画。 有什么建议吗?

7 个答案:

答案 0 :(得分:105)

有一种方法可以使用notifyDataSetChanged()

来保留精美的动画
  1. 您需要使用覆盖GridLayoutManager方法制作自己的supportsPredictiveItemAnimations() {/ 1}};

  2. 您需要true

  3. 我发现棘手的部分是您需要覆盖适配器的mAdapter.setHasStableIds(true)方法。它应该返回真正唯一的值,而不是getItemId()的直接函数。像position

  4. 这样的东西

    在我的情况下工作得很好 - 仅使用mItems.get(position).hashCode()

    添加,移除和移动项目的精美动画

答案 1 :(得分:5)

不,这是错的。首先,不能引用该方法返回后传递给onBindViewHolder的位置。当物品的位置发生变化时(由于物品移动等),RecyclerView不会重新绑定视图。

相反,您可以使用ViewHolder#getPosition()来返回更新的位置。

如果你解决了这个问题,那么你的移动代码应该可以正常工作。提供精彩的动画。

调用notifyDataSetChanged会阻止预测动画,因此请尽可能避免使用。有关详细信息,请参阅documentation

编辑(来自评论):从外部获取位置,从recyclerview获取子视图持有者,然后从vh获取位置。有关详细信息,请参阅RecyclerView api

答案 2 :(得分: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);

}

答案 3 :(得分:0)

您应该将方法移至print("{} hours, {} minutes, {} seconds".format(h, m, s), end = "\r") ,然后OnCreateViewHolder正常工作。

答案 4 :(得分:0)

通过将触摸动画添加到列表项的外部元素中,我可以保持触摸动画

<View
    android:foreground="?android:attr/selectableItemBackground"
...>

答案 5 :(得分:0)

我使用'notifyItemChanged(int position);'修复了它而不是'notifyDataSetChanged();'

我的适配器完美地显示了精美的动画,没有任何滞后

编辑:我从onBindViewHolder的位置得到了位置。

答案 6 :(得分:0)

如上文其他人所述,尽管您需要专门使用稳定的ID,但是在适配器上使用notifyDataSetChanged时可以制作动画。如果您的商品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()