调用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; } }
修复方案
核心问题原因
你当前代码的闪烁主要来自三个问题:
- 每次Firebase数据回调时都全量清空列表再重建,配合全局
notifyDataSetChanged()会让RecyclerView重绘所有可见item - 未给RecyclerView设置稳定ID,重绘时无法正确复用对应item的View
- 每次
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
相关产品推荐
相关产品推荐

