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

Android StaggeredGrid布局下RecyclerView分页失效、图片无法加载求助

问题修复方案

核心问题梳理

当前代码存在5个核心问题直接导致分页异常与图片加载失败:

  1. 无真正的分页逻辑:getData() 每次都全量拉取Firebase下所有uploads节点数据,且每次加载前清空全部已有列表数据,本质是全量反复刷新而非分页追加,大量重复的图片加载请求会导致Glide加载失败
  2. 加载状态未重置:isLoading 标记在数据加载完成后未重新设为false,只会触发一次加载逻辑
  3. Glide缩放属性冲突:onBindViewHolder中同时使用centerCrop()与fitCenter()两个互斥的缩放配置,会导致图片渲染异常
  4. 静态变量内存风险:PostAdapter中mUploads被声明为static,与Fragment的列表绑定后反复清空重插会导致StaggeredGridLayoutManager的item位置计算错误,出现空白、错位问题
  5. 重复注册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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 02:00:05