android recyclerView 适配器更新局部UI时,item闪烁

3,577 阅读2分钟

需求:在其他item拖拽到当前文件夹item时,我的白色边框要隐藏,移除后白色边框显示

解决:通过字段hideBorder,来标记是否隐藏白色边框,更改hideBorder字段,再调用notifyItemChanged通知item更新UI

现象:使用notifyItemChanged更新item,item会出现闪烁的情况

如图

image.png

解决方法:

1. 使用 ItemAnimator 来避免闪烁

如果第一种方法没有完全解决问题,可以自定义 RecyclerViewItemAnimator。禁用默认的动画可以减少闪烁。

recyclerView.setItemAnimator(null);

结果说明:虽然解决了闪烁问题,但是因为我的item拖拽时会有放大和缩小的动画,使用这种方式,会导致我的动画效果出现问题。

为了在避免闪烁的同时保留移动时的动画效果,可以自定义 ItemAnimator,只禁用更改动画而保留其他动画。下面是一个示例:

2. 自定义 ItemAnimator

创建一个自定义的 ItemAnimator 类,继承自 DefaultItemAnimator 并覆盖 animateChange 方法,以禁用更改动画。

public class CustomItemAnimator extends DefaultItemAnimator {

    @Override
    public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder,
                                 @NonNull RecyclerView.ViewHolder newHolder,
                                 int fromLeft, int fromTop, int toLeft, int toTop) {
        // Return false to indicate that no change animation should be performed
        return false;
    }

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        return super.animateAdd(holder);
    }

    @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        return super.animateRemove(holder);
    }

    @Override
    public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
        return super.animateMove(holder, fromX, fromY, toX, toY);
    }

    @Override
    public void runPendingAnimations() {
        super.runPendingAnimations();
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        super.endAnimation(item);
    }

    @Override
    public void endAnimations() {
        super.endAnimations();
    }

    @Override
    public boolean isRunning() {
        return super.isRunning();
    }
}

设置自定义 ItemAnimator

在你的 RecyclerView 中使用这个自定义的 ItemAnimator

recyclerView.setItemAnimator(new CustomItemAnimator());

结果说明:因为我的需求中,item有拖拽后置空的效果。这种方式会导致我的置空效果出现问题

最终选择方式

在使用 notifyItemChanged 时,RecyclerView 的 item 会闪烁,这是因为默认情况下,RecyclerView 会重新绑定整个 item 导致 UI 发生刷新。为了解决这个问题,可以采取以下方法:

1. 使用 notifyItemChanged(position, payload)

使用带有 payload 参数的 notifyItemChanged 方法。这样可以让 RecyclerView 只更新特定的部分,而不会重新绑定整个 item。

在你的 Adapter 中,重写 onBindViewHolder 方法,并处理 payload 参数。

@Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
    if (payloads.isEmpty()) {
        super.onBindViewHolder(holder, position, payloads);
    } else {
        // Handle the partial update here
        for (Object payload : payloads) {
            if (payload.equals("border")) {
                // Update the border visibility without refreshing the whole item
                holder.updateBorderVisibility();
            }
        }
    }
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    // Bind the entire item if payload is empty
    holder.bind(dataSet.get(position));
}

当你只需要更新边框显示和隐藏时,调用 notifyItemChanged 并传递一个自定义的 payload,比如 "border"

adapter.notifyItemChanged(position, "border");