Android Java中NestedScrollView内分页功能失效问题求助
问题:NestedScrollView内Firestore分页功能失效
我正在开发一款Android应用,从Firebase Firestore获取数据并实现分页功能,但NestedScrollView内的分页功能无法正常工作。
XML布局代码
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".HomeFragment"> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/Theme.AppCompat.Light"> <TextView android:id="@+id/location" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableStart="@drawable/ic_baseline_location_on_24red" android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24" android:fontFamily="@font/baloo_bhai" android:text="Your Location" android:textColor="#050505" android:textSize="20dp" /> </androidx.appcompat.widget.Toolbar> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/toolbar" android:padding="5dp" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <androidx.appcompat.widget.SearchView android:id="@+id/searchbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:background="@drawable/searchbardesign" android:focusable="false" app:iconifiedByDefault="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:queryHint="search here eg. rooms in delhi... " /> </RelativeLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipeFreshLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="?actionBarSize" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <androidx.core.widget.NestedScrollView android:id="@+id/nestedScrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="?actionBarSize" android:clipToPadding="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <org.imaginativeworld.whynotimagecarousel.ImageCarousel android:id="@+id/carousel" android:layout_width="match_parent" android:layout_height="150dp" android:layout_marginBottom="10dp" app:autoPlay="false" app:autoPlayDelay="3000" app:autoWidthFixing="true" app:carouselType="SHOWCASE" app:imageScaleType="fitXY" app:infiniteCarousel="true" app:scaleOnScroll="false" app:scalingFactor="0.15" app:showBottomShadow="false" app:showCaption="false" app:showNavigationButtons="false" app:showTopShadow="false" app:touchToPause="true" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/roomsRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:layoutAnimation="@anim/layout_animation_slide_from_bottom" android:orientation="vertical" android:scrollbars="vertical" /> <ProgressBar android:id="@+id/recyclerView_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" /> </LinearLayout> </androidx.core.widget.NestedScrollView> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.drawerlayout.widget.DrawerLayout>
Java代码
// onCreate内的代码 recyclerViewRooms.setNestedScrollingEnabled(false); nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { View view = (View) nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1); int diff = (view.getBottom() - (nestedScrollView.getHeight() + nestedScrollView.getScrollY())); if (diff == 0) { paginationCode(); } } }); // 分页方法 public void paginationCode() { FirebaseFirestore firebaseFirestore = FirebaseFirestore.getInstance(); Query productsRef = firebaseFirestore.collectionGroup("room_details"); Query query = productsRef.orderBy("location", Query.Direction.ASCENDING).limit(limit); progressBar.setVisibility(View.VISIBLE); query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> task) { if (task.isSuccessful()) { for (DocumentSnapshot d : task.getResult()) { RoomsDetails obj = d.toObject(RoomsDetails.class); roomsDetails.add(obj); } roomsAdapter.notifyDataSetChanged(); } lastVisible = task.getResult().getDocuments().get(task.getResult().size() - 1); RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { isScrolling = true; progressBar.setVisibility(View.GONE); } } @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); LinearLayoutManager linearLayoutManager = ((LinearLayoutManager) recyclerView.getLayoutManager()); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); int visibleItemCount = linearLayoutManager.getChildCount(); int totalItemCount = linearLayoutManager.getItemCount(); if (isScrolling && (firstVisibleItemPosition + visibleItemCount == totalItemCount) && !isLastItemReached) { isScrolling = false; progressBar.setVisibility(View.VISIBLE); Query nextQuery = productsRef.orderBy("location", Query.Direction.ASCENDING).startAfter(lastVisible).limit(limit); nextQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> t) { if (t.isSuccessful()) { for (DocumentSnapshot d : t.getResult()) { RoomsDetails productModel = d.toObject(RoomsDetails.class); roomsDetails.add(productModel); Toast.makeText(getContext(), "hey this is", Toast.LENGTH_SHORT).show(); } progressBar.setVisibility(View.GONE); roomsAdapter.notifyDataSetChanged(); lastVisible = t.getResult().getDocuments().get(t.getResult().size() - 1); if (t.getResult().size() < limit) { isLastItemReached = true; } } } }); } } }; recyclerViewRooms.addOnScrollListener(onScrollListener); } }); }
问题分析与修复方案
核心问题点
- 重复添加滚动监听器:每次调用
paginationCode()都会给RecyclerView添加新的OnScrollListener,导致同一滚动动作触发多次分页请求。 - 滚动逻辑冲突:同时监听NestedScrollView和RecyclerView的滚动,且RecyclerView已禁用嵌套滚动,导致滚动判断混乱。
- 初始加载与分页逻辑耦合:
paginationCode()同时处理初始加载和分页加载,逻辑冗余且容易出错。 - 缺少加载状态控制:没有标记当前是否正在加载数据,导致滚动到底部时重复触发请求。
- RecyclerView高度设置错误:在NestedScrollView内使用
match_parent高度会导致RecyclerView无法正确计算滚动位置。
修复后的代码
1. 修改XML中的RecyclerView高度
将RecyclerView的高度改为wrap_content,让NestedScrollView统一处理滚动:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/roomsRecyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutAnimation="@anim/layout_animation_slide_from_bottom" android:orientation="vertical" android:scrollbars="vertical" />
2. 重构Java代码,分离逻辑
// 全局变量 private boolean isLoading = false; private boolean isLastItemReached = false; private DocumentSnapshot lastVisible = null; private int limit = 10; // 每页加载数量 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化RecyclerView recyclerViewRooms.setNestedScrollingEnabled(false); recyclerViewRooms.setLayoutManager(new LinearLayoutManager(getContext())); roomsAdapter = new RoomsAdapter(roomsDetails); recyclerViewRooms.setAdapter(roomsAdapter); // 只添加一次滚动监听器 setupRecyclerViewScrollListener(); // 初始加载数据 loadInitialData(); } // 设置RecyclerView滚动监听器 private void setupRecyclerViewScrollListener() { recyclerViewRooms.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); // 标记用户正在滚动 if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { isLoading = false; } } @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // 避免重复加载、已加载完所有数据时不触发 if (!isLoading && !isLastItemReached) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); int visibleItemCount = layoutManager.getChildCount(); int totalItemCount = layoutManager.getItemCount(); int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition(); // 判断是否滚动到列表底部 if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount) { isLoading = true; progressBar.setVisibility(View.VISIBLE); loadNextPage(); } } } }); } // 初始加载第一页数据 private void loadInitialData() { isLoading = true; progressBar.setVisibility(View.VISIBLE); FirebaseFirestore db = FirebaseFirestore.getInstance(); Query query = db.collectionGroup("room_details") .orderBy("location", Query.Direction.ASCENDING) .limit(limit); query.get().addOnCompleteListener(task -> { if (task.isSuccessful() && task.getResult() != null) { roomsDetails.clear(); for (DocumentSnapshot doc : task.getResult()) { RoomsDetails obj = doc.toObject(RoomsDetails.class); roomsDetails.add(obj); } roomsAdapter.notifyDataSetChanged(); // 更新最后一条可见数据和是否加载完成 if (!task.getResult().isEmpty()) { lastVisible = task.getResult().getDocuments().get(task.getResult().size() - 1); isLastItemReached = task.getResult().size() < limit; } else { isLastItemReached = true; } } isLoading = false; progressBar.setVisibility(View.GONE); }); } // 加载下一页数据 private void loadNextPage() { if (lastVisible == null) return; FirebaseFirestore db = FirebaseFirestore.getInstance(); Query nextQuery = db.collectionGroup("room_details") .orderBy("location", Query.Direction.ASCENDING) .startAfter(lastVisible) .limit(limit); nextQuery.get().addOnCompleteListener(task -> { if (task.isSuccessful() && task.getResult() != null) { for (DocumentSnapshot doc : task.getResult()) { RoomsDetails obj = doc.toObject(RoomsDetails.class); roomsDetails.add(obj); } roomsAdapter.notifyDataSetChanged(); // 更新最后一条可见数据和是否加载完成 if (!task.getResult().isEmpty()) { lastVisible = task.getResult().getDocuments().get(task.getResult().size() - 1); isLastItemReached = task.getResult().size() < limit; } else { isLastItemReached = true; } } isLoading = false; progressBar.setVisibility(View.GONE); }); }
内容的提问来源于stack exchange,提问作者Puru Chaudhary
相关产品推荐
相关产品推荐

