Android NestedScrollView smoothScrollBy无法显示最后一项求助
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:weightSum="100"> <!--App Bar Layout Pane--> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="7" android:background="@drawable/AppBarBackground" android:weightSum="100"> <!--Unrelated XML Code--> </LinearLayout> <!--App Bar Layout Pane end--> <!--Body Pane--> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="93" android:weightSum="100" android:background="@drawable/AppBackground"> <!--Left Pane--> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="37" android:background="@color/whiteWithAlpha75"> <!--Unrelated XML Code--> </LinearLayout> <!--Left Pane end--> <!--Right Pane--> <FrameLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="43"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:weightSum="100"> <!--Right Pane Header--> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="8" android:weightSum="100" android:background="@color/whiteWithAlpha10" android:paddingLeft="15dp" android:paddingRight="15dp"> <TextView android:text="Item(s)" android:fontFamily="@string/fontFamily" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="24" android:textColor="@color/grayDark" android:gravity="center|left" android:textSize="@dimen/textSizeXSmall" /> <TextView android:text="Unit Price" android:fontFamily="@string/fontFamily" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="17" android:textColor="@color/grayDark" android:gravity="center" android:textSize="@dimen/textSizeXSmall" /> <TextView android:text="Quantity" android:fontFamily="@string/fontFamily" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="28" android:textColor="@color/grayDark" android:gravity="center" android:textSize="@dimen/textSizeXSmall" /> <TextView android:text="Discount" android:fontFamily="@string/fontFamily" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="16" android:textColor="@color/grayDark" android:gravity="center" android:textSize="@dimen/textSizeXSmall" /> <TextView android:text="Total" android:fontFamily="@string/fontFamily" android:gravity="center|right" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="15" android:textColor="@color/grayDark" android:textSize="@dimen/textSizeXSmall" /> </LinearLayout> <!--Right Pane Header end--> <!--Right Pane Item Selected--> <android.support.v4.widget.NestedScrollView android:id="@+id/scrollItemSelectedArea" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="92" android:paddingBottom="5dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:background="@color/whiteWithAlpha15"> <LinearLayout android:id="@+id/llItemSelectedArea" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="5dp" /> </android.support.v4.widget.NestedScrollView> <!--Right Pane Item Selected end--> <!--Right Pane Functions--> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="39" android:weightSum="100"> <!--Unrelated XML Code--> </LinearLayout> <!--Right Pane Functions end--> </LinearLayout> </FrameLayout> <!--Right Pane end--> </LinearLayout> <!--Body Pane end--> </LinearLayout>
问题
当列表项高度达到NestedScrollView的高度上限时,理应自动滚动以显示最后选中的项。我每次选中项时都会调用smoothScrollBy方法,但出现异常:例如ScrollView可视区域仅能容纳5项,添加第6项时不会向上滚动,导致第6项无法显示;添加第7项时,第6项显示出来,但第7项又被隐藏。我的ScrollView高度设为match_parent,底部无重叠布局,不清楚是否存在额外空间。请问如何修复该问题?
以下是添加新项的位置:
解决方案
我之前遇到过一模一样的问题!核心原因是你调用smoothScrollBy的时候,新添加的Item还没完成布局测量,NestedScrollView根本拿不到正确的滚动距离,自然就滚不准了。给你几个亲测有效的解决办法:
1. 用post延迟执行滚动
添加完新Item后,把滚动操作放到UI线程的消息队列末尾,等新View完成布局再执行:
// 先添加新项到llItemSelectedArea llItemSelectedArea.addView(newItemView); // 延迟滚动,确保布局完成 scrollItemSelectedArea.post(() -> { // 直接滚动到子布局的底部,也就是最后一项的位置 scrollItemSelectedArea.smoothScrollTo(0, llItemSelectedArea.getBottom()); });
如果还是想用smoothScrollBy,可以计算需要滚动的偏移量:
llItemSelectedArea.addView(newItemView); scrollItemSelectedArea.post(() -> { int requiredScroll = llItemSelectedArea.getHeight() - scrollItemSelectedArea.getHeight(); if (requiredScroll > 0) { scrollItemSelectedArea.smoothScrollBy(0, requiredScroll); } });
2. 检查NestedScrollView的布局参数
你的NestedScrollView用了layout_weight="92",先确认父LinearLayout的weightSum是正确的(看你的XML里父布局是weightSum="100",这个没问题)。另外,注意到你加了app:layout_behavior="@string/appbar_scrolling_view_behavior"——如果你的布局里没有AppBarLayout,这个behavior完全没必要,反而可能干扰布局计算,建议删掉试试。
3. 直接用fullScroll快速滚动
如果不需要平滑滚动,NestedScrollView有个快捷方法可以瞬间滚到底部:
scrollItemSelectedArea.post(() -> { scrollItemSelectedArea.fullScroll(View.FOCUS_DOWN); });
这个方法会自动把最后一个Item带到可视区域。
4. 监听布局变化
要是上面的方法都不行,可以给子LinearLayout加个布局变化监听器,每次布局更新就滚动到底部:
llItemSelectedArea.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { scrollItemSelectedArea.smoothScrollTo(0, llItemSelectedArea.getBottom()); });
最推荐的是第一种方法,用post延迟滚动,几乎能解决90%以上的ScrollView滚动不准问题,因为它等新View完全布局好了再计算滚动距离,自然就准确了。
内容的提问来源于stack exchange,提问作者jace

