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

RecyclerView滚动监听异常:未滚动却自动连续加载Firebase数据

解决RecyclerView自动连续加载更多的问题

从你的Logcat和代码来看,问题出在数据更新后RecyclerView重布局触发滚动监听,再加上Loading状态的设置时机不对,导致每次数据加载完成后,立刻又触发下一次加载,形成了循环。下面是具体的修复步骤:

1. 修正初始加载的Loading状态与数据更新逻辑

你在firstEventListener的onChildAdded里每次添加子项都设置Loading = true,这会干扰初始加载的状态管理。应该把数据更新的通知统一放在加载完成后,并且只在初始加载开始和结束时设置Loading:

// 初始化首次数据拉取的函数
private void firstEventListener() {
    Loading = true; // 初始加载开始时标记为加载中
    Log.d(TAG, "onCreateView - Loading = " + Loading);
    
    FirebaseDatabase.getInstance().getReference().child("blogs").addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            mTotalChildren = dataSnapshot.getChildrenCount();
        }
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) { }
    });

    final Query BlogQuery = FirebaseDatabase.getInstance().getReference().child("blogs").orderByChild("createdAt").limitToLast(3);
    childEventListenerMain = new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot ds, String s) {
            Log.d(TAG, "First get blogs instance");
            BlogObject addblog = ds.getValue(BlogObject.class);
            if (addblog != null) {
                // 简化对象创建,直接复用已获取的对象
                BlogObject addBlog = new BlogObject(
                    addblog.getImage(), 
                    addblog.getTitle(), 
                    addblog.getTimeStamp(), 
                    addblog.getCreatedAt()
                );
                blogArray.add(addBlog);
                mLastKey = blogArray.get(0).getCreatedAt();
            }
        }
        // 其他ChildEventListener方法保持不变
    };
    BlogQuery.addChildEventListener(childEventListenerMain);

    ValueEventListener BlogChildSingleListener = new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            rvaVertical.notifyDataSetChanged(); // 所有初始数据加载完成后统一更新列表
            rvVertical.scrollToPosition(blogArray.size() - 1);
            Loading = false; // 初始加载完成,标记为可加载状态
            Log.d(TAG, "Initial load completed - Loading = " + Loading);
        }
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            Loading = false; // 加载失败也要恢复状态,避免卡死
        }
    };
    BlogQuery.addListenerForSingleValueEvent(BlogChildSingleListener);
}

2. 优化滚动监听的触发条件

原来的滚动监听没有区分“用户主动向下滚动”和“数据更新导致的滚动”,而且Loading的标记时机太晚。我们增加**向下滚动(dy>0)**的判断,并且在触发加载前立刻标记Loading = true:

private void addOnScrollListener() {
    rvVertical.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            Log.d(TAG, "Just Entered onScrolled and Loading = " + Loading);
            
            // 只在以下条件满足时触发加载:
            // 1. 用户向下滚动(dy>0)
            // 2. 未处于加载状态
            // 3. 已经滚动到列表底部(无法再向下滚动)
            if (dy > 0 && !Loading && !rvVertical.canScrollVertically(1)) {
                LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                if (linearLayoutManager != null && 
                    blogArray.size() >= 3 && 
                    linearLayoutManager.findLastCompletelyVisibleItemPosition() == blogArray.size() - 1 && 
                    blogArray.size() < mTotalChildren) {
                    
                    Loading = true; // 立刻标记为加载中,防止重复触发
                    getMoreBlogs();
                    Log.d(TAG, "Right After getMoreBlogs gets called. Loading = " + Loading);
                }
            }
        }
    });
}

3. 修复加载更多的逻辑与重复数据问题

你原来的getMoreBlogs里用endAt(mLastKey)会获取到之前的最后一条数据,导致重复。另外,不要在onChildAdded里设置Loading,而是在函数开头就标记,并且在加载完成后统一恢复状态:

public void getMoreBlogs() {
    Loading = true; // 加载开始时标记为加载中
    tempList.clear();
    
    // 用endAt(mLastKey - 1)避免获取重复的最后一条数据
    Query getMoreQuery = FirebaseDatabase.getInstance().getReference().child("blogs")
        .orderByChild("createdAt")
        .endAt(mLastKey - 1)
        .limitToLast(3);

    childEventListenerPager = new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot ds, String s) {
            BlogObject blogObject = ds.getValue(BlogObject.class);
            if (blogObject != null) {
                BlogObject addBlog = new BlogObject(
                    blogObject.getImage(), 
                    blogObject.getTitle(), 
                    blogObject.getTimeStamp(), 
                    blogObject.getCreatedAt()
                );
                tempList.add(addBlog);
            }
        }
        // 其他ChildEventListener方法保持不变
    };
    getMoreQuery.addChildEventListener(childEventListenerPager);

    ValueEventListener addtoAdapter = new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            if (!tempList.isEmpty()) { // 只有获取到新数据时才更新列表
                blogArray.addAll(0, tempList);
                mLastKey = blogArray.get(0).getCreatedAt();
                rvaVertical.notifyDataSetChanged();
            }
            Loading = false; // 加载完成,恢复可加载状态
            Log.d(TAG, "More load completed - Loading = " + Loading);
        }
        @Override
        public void onCancelled(DatabaseError databaseError) {
            Loading = false; // 加载取消/失败也要恢复状态
        }
    };
    getMoreQuery.addListenerForSingleValueEvent(addtoAdapter);
}

额外提醒:清理Firebase监听避免内存泄漏

记得在页面销毁时移除Firebase的监听,防止内存泄漏:

@Override
public void onDestroyView() {
    super.onDestroyView();
    if (childEventListenerMain != null) {
        FirebaseDatabase.getInstance().getReference().child("blogs").removeEventListener(childEventListenerMain);
    }
    if (childEventListenerPager != null) {
        FirebaseDatabase.getInstance().getReference().child("blogs").removeEventListener(childEventListenerPager);
    }
}

这些修改应该能解决你遇到的自动连续加载问题,只有当用户主动滚动到底部时才会触发一次加载更多的逻辑。

内容的提问来源于stack exchange,提问作者Ed Delacruz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:33:18