如何在notifyItemChanged(pos)之后阻止RecyclerView项目闪烁?

时间:2017-02-21 23:02:52

标签: android android-recyclerview

我目前有一个回收站视图,其数据每5秒更新一次。要更新列表中的数据,我正在使用

notifyItemChanged(position);
notifyItemRangeChanged(position, mList.size());

每次调用notifyItemChanged()时,我的recycler视图中的项都会正确更新,但是它会闪烁,因为这会导致再次调用onBindViewHolder。所以它似乎每次都是一个新的负载。如果可能的话,我怎样才能防止这种情况发生?

9 个答案:

答案 0 :(得分:36)

RecyclerView内置动画,通常可以添加漂亮的抛光效果。在您的情况下,您将要禁用它们:

((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(false);

(默认的回收者视图动画师应该已经是SimpleItemAnimator的实例)

答案 1 :(得分:18)

您可以停用项目动画。

mRecyclerView.setItemAnimator(null);

答案 2 :(得分:1)

问题可能不是来自动画,而是来自not stable id of the list item

  

要使用稳定ID,您需要:

  -setHasStableIds(true)
  在RecyclerView.Adapter中,我们需要设置setHasStableIds(true);。 true表示此适配器将发布唯一值作为数据集中项目的键。通知数据更改后,适配器可以使用该密钥指示它们是否相同。

  -覆盖getItemId(int position)
  然后,我们必须重写getItemId(int position),以返回针对所定位项目的已确定长度。我们需要确保没有其他具有相同返回ID的商品数据。

is here的解决方案来源。

答案 3 :(得分:1)

我阅读了以下解决方案并实现了该解决方案,它在我测试的每台设备上都能正常工作。

  • 克隆默认动画师类。
  • 在animateChange()方法中,在3行以下注释,

    final float prevAlpha = oldHolder.itemView.getAlpha();
    .
    .
    oldHolder.itemView.setAlpha(prevAlpha);
    .
    .
    newHolder.itemView.setAlpha(0);
    
  • 将recyclerview项目动画器设置为克隆类的动画器。

///注:我确实了解该解决方案在不更改新持有人的alpha值的情况下如何工作,但这不是我的解决方案,我在stackoverflow本身上阅读了此内容,但由于某种原因找不到了。分享此内容可以帮助其他开发人员。

答案 4 :(得分:1)

此外,尝试在Main范围内的单独启动的协程作业中运行每个notifyItemChanged。对于我来说,它可以很好地消除眨眼。

for (i in 0 until adapter.itemCount) {
    CoroutineScope(Main).launch {
        adapter.notifyItemChanged(i)
    }
}

答案 5 :(得分:0)

在适配器中使用stableId

调用adapter.setHasStableIds(true)并在您的适配器类中覆盖getItemId(int position)方法。

还要为每个项目从getItemId(int position)返回一些唯一的ID。不要只是简单地返回位置。

答案 6 :(得分:0)

就我而言,以上任何内容或其他具有相同问题的stackoverflow问题的答案均​​无效。

好吧,每次单击该项目时,我都在使用自定义动画,为此我调用notifyItemChanged(int position,Object Payload)将有效负载传递给我的CustomAnimator类。

注意,RecyclerView Adapter中有2种onBindViewHolder(...)方法可用。 具有3个参数的onBindViewHolder(...)方法将始终在具有2个参数的onBindViewHolder(...)方法之前调用。

通常,我们总是重写具有2个参数的onBindViewHolder(...)方法,而问题的主要根源是我在做同样的事情, 每次调用notifyItemChanged(...)时,都会调用我们的onBindViewHolder(...)方法,在该方法中,我使用Picasso将图像加载到ImageView中,这就是无论是否从内存中重新加载图像的原因或通过互联网。在加载之前,它向我显示了占位符图像,这是当我单击itemview时闪烁1秒钟的原因。

稍后,我还将重写另一个具有3个参数的onBindViewHolder(...)方法。在这里,我检查有效载荷列表是否为空,然后返回此方法的超类实现,否则,如果存在有效载荷,我只是将holder的itemView的alpha值设置为1。

是的,可悲的是浪费了一整天之后,我得到了解决问题的方法!

这是我的onBindViewHolder(...)方法的代码:

onBindViewHolder(...)具有2个参数:

@Override
public void onBindViewHolder(@NonNull RecyclerAdapter.ViewHolder viewHolder, int position) {
            Movie movie = movies.get(position);

            Picasso.with(context)
                    .load(movie.getImageLink())
                    .into(viewHolder.itemView.posterImageView);
    }

onBindViewHolder(...)带有3个参数:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads);
        } else {
            holder.itemView.setAlpha(1);
        }
    }

这是我在onCreateViewHolder(...)的viewHolder的itemView的onClickListener中调用的方法的代码:

private void onMovieClick(int position, Movie movie) {
        Bundle data = new Bundle();
        data.putParcelable("movie", movie);

        // This data(bundle) will be passed as payload for ItemHolderInfo in our animator class
        notifyItemChanged(position, data);
    }

注意:您可以通过从onCreateViewHolder(...)调用viewHolder的getAdapterPosition()方法来获得此位置。

我还重写了getItemId(int position)方法,如下所示:

@Override
public long getItemId(int position) {
    Movie movie = movies.get(position);
    return movie.getId();
}

并在活动中在我的适配器对象上调用setHasStableIds(true);

希望以上方法均不能解决问题!

答案 7 :(得分:0)

如果要保留RecyclerView的动画并避免同时闪烁项目,可以按照以下步骤操作:

1。直接更改视图组件状态 2.直接在适配器中更改数据 3.无需通过调用notifyItemChanged()

手动刷新UI

直接更改视图不会立即进行更改,更好的方法是使用payload进行更改。

在适配器中:

override fun onBindViewHolder(
        holder: RecyclerView.ViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        // Using payload to refresh partly
        if (payloads.isNullOrEmpty()) {
            // refresh all 
            onBindViewHolder(holder, position)
            return
        }
        // just change one component's status which would not "flash".
        (holder as YourHolder).apply{
            // make you view change
            // etc. checkBox.isChecked = true
        }
    }

并使用它:


yourAdapter.notifyItemChanged(position, "sample_pay_load")

这里是notifyitemchanged's Doc

答案 8 :(得分:0)

之所以发生这种情况,是因为Adapter正在创建新的ViewHolder并尝试在新旧的ViewHolders之间建立动画。这就是为什么所有“禁用动画”答案在技术上都起作用的原因。

相反,您只能告诉RecyclerViewcanReuseUpdatedViewHolder()一起使用Viewholder。看到类似问题的答案: https://stackoverflow.com/a/60427676/1650674