在notifyDatasetChanged()之后RecyclerView闪烁

Ali*_*Ali 102 android android-fragments android-recyclerview

我有一个RecyclerView,它从API加载一些数据,包括一个图像URL和一些数据,我使用networkImageView来延迟加载图像.

@Override
public void onResponse(List<Item> response) {
   mItems.clear();
   for (Item item : response) {
      mItems.add(item);
   }
   mAdapter.notifyDataSetChanged();
   mSwipeRefreshLayout.setRefreshing(false);
}
Run Code Online (Sandbox Code Playgroud)

这是适配器的实现:

public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
        if (isHeader(position)) {
            return;
        }
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        MyViewHolder holder = (MyViewHolder) viewHolder;
        final Item item = mItems.get(position - 1); // Subtract 1 for header
        holder.title.setText(item.getTitle());
        holder.image.setImageUrl(item.getImg_url(), VolleyClient.getInstance(mCtx).getImageLoader());
        holder.image.setErrorImageResId(android.R.drawable.ic_dialog_alert);
        holder.origin.setText(item.getOrigin());
    }
Run Code Online (Sandbox Code Playgroud)

问题是当我们在recyclerView中刷新时,它在开始时很短暂地看起来很奇怪.

我只使用了GridView/ListView,它按预期工作.没有瞎眼.

RecycleView的配置onViewCreated of my Fragment:

mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        mGridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
        mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                return mAdapter.isHeader(position) ? mGridLayoutManager.getSpanCount() : 1;
            }
        });

        mRecyclerView.setAdapter(mAdapter);
Run Code Online (Sandbox Code Playgroud)

有人遇到过这样的问题吗?可能是什么原因?

小智 109

尝试在RecyclerView.Adapter中使用稳定的ID

setHasStableIds(true)和覆盖getItemId(int position).

如果没有稳定的ID,notifyDataSetChanged()ViewHolders通常会分配到不同的位置.这就是我案件中闪烁的原因.

你可以在这里找到一个很好的解释.

  • 工作得很好.吓人的 (3认同)
  • 我添加了 setHasStableIds(true),它解决了闪烁问题,但在我的 GridLayout 中,项目在滚动时仍然会改变位置。我应该在 getItemId() 中覆盖什么?谢谢 (3认同)
  • 我们应该如何生成ID? (3认同)

Sab*_*med 97

根据这个问题页面....它是默认的recycleview项目更改动画...你可以关闭它..试试这个

recyclerView.getItemAnimator().setSupportsChangeAnimations(false);
Run Code Online (Sandbox Code Playgroud)

更新最新版本

引自Android开发人员博客:

请注意,此新API不向后兼容.如果您之前实现了ItemAnimator,则可以扩展SimpleItemAnimator,它通过包装新API来提供旧API.您还会注意到一些方法已从ItemAnimator中完全删除.例如,如果您正在调用recyclelerView.getItemAnimator().setSupportsChangeAnimations(false),则此代码将不再编译.您可以将其替换为:

ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
  ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
Run Code Online (Sandbox Code Playgroud)

  • Kotlin :( recyclerView.itemAnimator as?SimpleItemAnimator)?. supportsChangeAnimations = false (6认同)
  • @sabeer仍然是同一个问题.它没有解决问题. (4认同)
  • @delive让我知道你是否找到了解决方法 (3认同)

Ham*_*boh 39

这简单有效:

recyclerView.getItemAnimator().setChangeDuration(0);
Run Code Online (Sandbox Code Playgroud)

  • 停止所有动画添加和删除 (2认同)

Wes*_*ely 10

我有一些问题从一些网址加载图像,然后imageView闪烁.通过使用解决

notifyItemRangeInserted()    
Run Code Online (Sandbox Code Playgroud)

代替

notifyDataSetChanged()
Run Code Online (Sandbox Code Playgroud)

这避免了重新加载那些未更改的旧数据.


小智 9

尝试此操作以禁用默认动画

ItemAnimator animator = recyclerView.getItemAnimator();

if (animator instanceof SimpleItemAnimator) {
  ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
Run Code Online (Sandbox Code Playgroud)

这是自Android支持23以来禁用动画的新方法

这种旧方法适用于旧版本的支持库

recyclerView.getItemAnimator().setSupportsChangeAnimations(false)
Run Code Online (Sandbox Code Playgroud)


Rob*_*Pal 6

科特林解决方案:

(recyclerViewIdFromXML.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
Run Code Online (Sandbox Code Playgroud)


Pet*_*ile 5

Recyclerview 使用 DefaultItemAnimator 作为默认动画制作器。正如您从下面的代码中看到的,它们会在项目更改时更改视图持有者的 alpha:

@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
    ...
    final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
    ...
    ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
    if (newHolder != null) {
        ....
        ViewCompat.setAlpha(newHolder.itemView, 0);
    }
    ...
    return true;
}
Run Code Online (Sandbox Code Playgroud)

我想保留其余的动画但删除“闪烁”,所以我克隆了 DefaultItemAnimator 并删除了上面的 3 条 alpha 线。

要使用新的动画师,只需在 RecyclerView 上调用 setItemAnimator():

mRecyclerView.setItemAnimator(new MyItemAnimator());
Run Code Online (Sandbox Code Playgroud)


Gre*_*ory 5

在 Kotlin 中,您可以对 RecyclerView 使用“类扩展”:

fun RecyclerView.disableItemAnimator() {
    (itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
}

// sample of using in Activity:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // ...
    myRecyclerView.disableItemAnimator()
    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 禁用动画并不能解决问题。 (2认同)

yig*_*git 4

假设mItems是支持您的集合Adapter,为什么要删除所有内容并重新添加?您基本上是在告诉它一切都已更改,因此 RecyclerView 会重新绑定所有视图,而不是我假设图像库无法正确处理它,即使它是相同的图像 url,它仍然会重置视图。也许他们为 AdapterView 提供了一些解决方案,以便它在 GridView 中正常工作。

不要调用notifyDataSetChanged会导致重新绑定所有视图的调用,而是调用粒度通知事件(通知添加/删除/移动/更新),以便 RecyclerView 将仅重新绑定必要的视图,并且不会出现任何闪烁。

  • 或者这只是 RecyclerView 中的一个“bug”?显然,如果过去 6 年在 AbsListView 上工作得很好,而现在在 RecyclerView 上却不行了,那就意味着 RecyclerView 出了问题,对吧?:) 快速查看表明,当您刷新 ListView 和 GridView 中的数据时,它们会跟踪视图+位置,因此当您刷新时,您将获得完全相同的 viewholder。而 RecyclerView 会打乱视图持有者,这会导致闪烁。 (4认同)