Android StaggeredGrid布局下RecyclerView分页失效、图片无法加载求助
问题修复方案
核心问题梳理
当前代码存在5个核心问题直接导致分页异常与图片加载失败:
- 无真正的分页逻辑:
getData()每次都全量拉取Firebase下所有uploads节点数据,且每次加载前清空全部已有列表数据,本质是全量反复刷新而非分页追加,大量重复的图片加载请求会导致Glide加载失败 - 加载状态未重置:
isLoading标记在数据加载完成后未重新设为false,只会触发一次加载逻辑 - Glide缩放属性冲突:
onBindViewHolder中同时使用centerCrop()与fitCenter()两个互斥的缩放配置,会导致图片渲染异常 - 静态变量内存风险:
PostAdapter中mUploads被声明为static,与Fragment的列表绑定后反复清空重插会导致StaggeredGridLayoutManager的item位置计算错误,出现空白、错位问题 - 重复注册Firebase监听:每次触发加载都会调用
addValueEventListener注册新的监听,未移除旧监听会导致多次回调,列表数据反复被覆盖
修复步骤与代码示例
1. 新增分页基础变量
在Fragment中新增分页相关变量:
// 分页拉取数量,可按需调整 private static final int PAGE_SIZE = 10; // 记录上一页最后一条数据的key,用于分页偏移 private String lastLoadedKey = null; // 是否还有更多数据可加载 private boolean hasMoreData = true;
2. 优化滚动监听逻辑
仅在上滑时触发分页,同时增加无更多数据的判断:
postRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // 仅上滑、非加载中、还有更多数据时才触发分页 if (dy <= 0 || isLoading || !hasMoreData) return; assert linearLayoutManager != null; int[] lastVisibleItemPositions = linearLayoutManager.findLastVisibleItemPositions(null); int lastVisibleItem = getLastVisibleItem(lastVisibleItemPositions); int totalItemCount = linearLayoutManager.getItemCount(); if (totalItemCount <= (lastVisibleItem + visibleThreshold)) { isLoading = true; getData(); } } });
3. 重写getData实现分页加载
使用Firebase的分页查询逻辑,仅追加新数据而非全量清空:
private void getData() { contentLoadingProgressBar.setVisibility(View.VISIBLE); Query query; // 首次加载/无偏移时拉取第一页,否则从上次记录的key开始拉取 if (lastLoadedKey == null) { query = databaseReference.orderByKey().limitToFirst(PAGE_SIZE); } else { query = databaseReference.orderByKey().endAt(lastLoadedKey).limitToFirst(PAGE_SIZE + 1); } // 改用单次查询addListenerForSingleValueEvent,避免重复监听 query.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { contentLoadingProgressBar.setVisibility(View.GONE); // 首次加载隐藏shimmer if (lastLoadedKey == null) { shimmerFrameLayout.stopShimmer(); shimmerFrameLayout.setVisibility(View.GONE); postRecyclerView.setVisibility(View.VISIBLE); } List<Upload> newUploads = new ArrayList<>(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Upload upload = dataSnapshot.getValue(Upload.class); Objects.requireNonNull(upload).setmKey(dataSnapshot.getKey()); newUploads.add(upload); } // 处理分页边界 if (newUploads.size() > 0) { // 非首次加载时要去掉重复的最后一条数据 if (lastLoadedKey != null) { newUploads.remove(0); } if (newUploads.size() == 0) { hasMoreData = false; } else { // 更新最后加载的key lastLoadedKey = newUploads.get(newUploads.size() - 1).getmKey(); // 追加数据到列表末尾 mUploads.addAll(newUploads); postsAdapter.notifyItemRangeInserted(mUploads.size() - newUploads.size(), newUploads.size()); } } else { hasMoreData = false; } // 重置加载状态 isLoading = false; } @Override public void onCancelled(@NonNull DatabaseError error) { contentLoadingProgressBar.setVisibility(View.GONE); isLoading = false; } }); }
4. 修复Adapter的问题
移除mUploads的static修饰,修正Glide配置:
// 把static去掉,改为私有变量 private List<Upload> mUploads;
@Override public void onBindViewHolder(@NonNull PostViewHolder holder, int position) { Upload uploadCurrent = mUploads.get(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); Glide.with(mcontext) .load(uploadCurrent.getmImageUrl()) .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) .placeholder(shimmerDrawable) // 保留一个缩放配置即可,瀑布流推荐用centerCrop .centerCrop() .into(holder.imageView); }
5. 补充初始化逻辑
在Fragment的onCreateView中初始化完Adapter后调用首次加载:
postRecyclerView.setAdapter(postsAdapter); postsAdapter.setOnItemClickListener(Profile_Fragment.this); // 触发首次分页加载 getData(); return view;
内容的提问来源于stack exchange,提问作者Vasant Raval
相关产品推荐
相关产品推荐

