You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用notifyDataSetChanged()时RecyclerView出现闪烁问题如何解决?

问题描述

在版主将此问题标记为重复问题前,请先阅读以下内容:
我已尝试多种现有解决方案但均未生效,例如禁用动画后闪烁问题依旧存在;我尝试参考相关教程进行修复,但我的代码没有使用getItemID方法,不知道如何适配该方案。


相关代码

Home_Fragment.java

注:仅提供认为必要的部分代码,若需要查看完整代码可告知更新

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_home, container, false);
    postRecyclerView.setItemAnimator(null);
    getData();
    return view;
}

private void getData() {
    databaseReference.addValueEventListener(new ValueEventListener() {
        @SuppressLint("NotifyDataSetChanged")
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            if (snapshot.exists()) {
                shimmerFrameLayout.stopShimmer();
                shimmerFrameLayout.setVisibility(View.GONE);
                postRecyclerView.setVisibility(View.VISIBLE);
                mUploads.clear();
                for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                    Upload upload = dataSnapshot.getValue(Upload.class);
                    assert upload != null;
                    upload.setmKey(dataSnapshot.getKey());
                    mUploads.add(upload);
                }
            }
            postsAdapter.setUploads(mUploads);
            //notify the adapter
            postsAdapter.notifyDataSetChanged();
            loading = true;
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
            loading = true;
        }
    });
}

PostAdapter_Home.java

public class PostAdapter_Home extends RecyclerView.Adapter<PostAdapter_Home.PostViewHolder> {
    public static List<Upload> mUploads;
    public Context mcontext;

    public PostAdapter_Home(Context context, List<Upload> uploads) {
        mUploads = uploads;
        mcontext = context;
    }

    @NonNull
    @Override
    public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        view = LayoutInflater.from(mcontext).inflate(R.layout.ex_home, parent, false);
        return new PostViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
        Shimmer shimmer = new Shimmer.ColorHighlightBuilder()
                .setBaseColor(Color.parseColor("#F3F3F3"))
                .setBaseAlpha(1)
                .setHighlightColor(Color.parseColor("#E7E7E7"))
                .setHighlightAlpha(1)
                .setDropoff(50)
                .build();
        ShimmerDrawable shimmerDrawable = new ShimmerDrawable();
        shimmerDrawable.setShimmer(shimmer);
        Upload uploadCurrent = mUploads.get(position);
        Glide.with(mcontext)
                .load(uploadCurrent.getmImageUrl())
                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
                .placeholder(shimmerDrawable)
                .centerCrop()
                .fitCenter()
                .into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return mUploads.size();
    }

    public void setUploads(List<Upload> uploads){
        mUploads=uploads;
    }

    public static class PostViewHolder extends RecyclerView.ViewHolder {
        private final ShapeableImageView imageView;

        public PostViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.imagePostHome);
        }
    }
}

Upload.java

package com.example.myappnotfinal.AdaptersAndMore;

import com.google.firebase.database.Exclude;

public class Upload {
    private String mImageUrl;
    private String mKey;
    private String mUserName;
    private String mComment;

    public Upload() {

    }

    public Upload(String imageUrl) {
        mImageUrl = imageUrl;
    }

    public String getmUserName() {
        return mUserName;
    }

    public void setmUserName(String mUserName) {
        this.mUserName = mUserName;
    }

    public String getmComment() {
        return mComment;
    }

    public void setmComment(String mComment) {
        this.mComment = mComment;
    }

    public String getmImageUrl() {
        return mImageUrl;
    }

    public void setmImageUrl(String mImageUrl) {
        this.mImageUrl = mImageUrl;
    }

    @Exclude
    public String getmKey() {
        return mKey;
    }

    @Exclude
    public void setmKey(String Key) {
        this.mKey = Key;
    }
}

修复方案

核心问题原因

你当前代码的闪烁主要来自三个问题:

  1. 每次Firebase数据回调时都全量清空列表再重建,配合全局notifyDataSetChanged()会让RecyclerView重绘所有可见item
  2. 未给RecyclerView设置稳定ID,重绘时无法正确复用对应item的View
  3. 每次onBindViewHolder都新建闪烁占位符,导致Glide加载时重复触发占位逻辑

具体修改步骤

1. 给Adapter添加稳定ID

修改PostAdapter_Home.java的构造方法,同时重写getItemId方法:

public PostAdapter_Home(Context context, List<Upload> uploads) {
    mUploads = uploads;
    mcontext = context;
    // 开启稳定ID
    setHasStableIds(true);
}

// 新增该方法
@Override
public long getItemId(int position) {
    // 直接用Firebase返回的唯一键的哈希值作为item的稳定ID
    return mUploads.get(position).getmKey().hashCode();
}

2. 替换全局刷新为差分刷新

删除Home_Fragment.java中onDataChange方法里的postsAdapter.notifyDataSetChanged(),修改PostAdapter_Home.java的setUploads方法,用DiffUtil实现局部刷新:

public void setUploads(List<Upload> newUploads){
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
        @Override
        public int getOldListSize() {
            return mUploads.size();
        }

        @Override
        public int getNewListSize() {
            return newUploads.size();
        }

        @Override
        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
            // 用唯一键判断是否为同一个item
            return mUploads.get(oldItemPosition).getmKey().equals(newUploads.get(newItemPosition).getmKey());
        }

        @Override
        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
            Upload oldItem = mUploads.get(oldItemPosition);
            Upload newItem = newUploads.get(newItemPosition);
            // 对比所有业务字段判断内容是否变化,可根据你的需求扩展
            return oldItem.getmImageUrl().equals(newItem.getmImageUrl())
                    && oldItem.getmUserName().equals(newItem.getmUserName())
                    && oldItem.getmComment().equals(newItem.getmComment());
        }
    });
    mUploads = newUploads;
    diffResult.dispatchUpdatesTo(this);
}

3. 优化Glide占位符逻辑

将原来每次绑定都新建的闪烁占位符改为ViewHolder的成员变量,仅初始化一次,修改PostAdapter_Home.java的PostViewHolder类:

public static class PostViewHolder extends RecyclerView.ViewHolder {
    private final ShapeableImageView imageView;
    // 新增占位符成员变量
    private final ShimmerDrawable shimmerDrawable;

    public PostViewHolder(@NonNull View itemView) {
        super(itemView);
        imageView = itemView.findViewById(R.id.imagePostHome);
        // 仅初始化一次占位符
        Shimmer shimmer = new Shimmer.ColorHighlightBuilder()
                .setBaseColor(Color.parseColor("#F3F3F3"))
                .setBaseAlpha(1)
                .setHighlightColor(Color.parseColor("#E7E7E7"))
                .setHighlightAlpha(1)
                .setDropoff(50)
                .build();
        shimmerDrawable = new ShimmerDrawable();
        shimmerDrawable.setShimmer(shimmer);
    }

    public ShimmerDrawable getShimmerDrawable() {
        return shimmerDrawable;
    }
}

然后修改onBindViewHolder中的Glide加载逻辑,直接复用ViewHolder中的占位符:

@Override
public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
    Upload uploadCurrent = mUploads.get(position);
    Glide.with(mcontext)
            .load(uploadCurrent.getmImageUrl())
            .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
            // 复用已初始化的占位符
            .placeholder(holder.getShimmerDrawable())
            .centerCrop()
            .fitCenter()
            .into(holder.imageView);
}

可选优化

如果不需要实时监听Firebase数据变动,可将addValueEventListener替换为addListenerForSingleValueEvent,避免频繁回调触发不必要的列表刷新。


内容的提问来源于stack exchange,提问作者Vasant Raval

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 18:45:07