Android ListView最后一项无法完全滚动显示适配问题
问题原因
最后一项显示不全、需要两次按方向键才能滚到底的问题,是三个布局配置问题叠加导致的:
- 单位混用:整个布局绝大多数尺寸用px定义,唯独ListView加了
android:layout_marginBottom="5dp"的dp单位边距,不同屏幕密度下这个边距的实际像素值不固定,直接导致ListView的可用高度和你固定80px高的列表项不是整数倍关系,底部必然出现遮挡或者多余留白。 - 垂直渐变边占用绘制空间:你给ListView配置了
android:fadingEdge="vertical"和android:requiresFadingEdge="vertical",系统会在ListView底部默认预留几十像素的渐变遮罩区域,计算滚动偏移和焦点位置时,这部分区域不会算成有效可视区,所以第一次按向下键焦点落到最后一项时,列表只会滚动到最后一项顶部对齐渐变遮罩上沿的位置,必须再按一次才能把整个项露出来。 - 布局属性冗余无效:包裹ListView的LinearLayout没有写
android:orientation属性,默认是水平排列模式,你给ListView加的android:layout_weight="1"在水平布局下只控制宽度权重,对高度适配完全不起作用,反而可能干扰系统的布局尺寸计算。
修复步骤
- 修正尺寸和边距配置
统一所有尺寸单位,要么全用px要么全用dp,不要混写。如果要保留底部边距,把ListView的layout_marginBottom单位改成和全局一致的px,同时调整父容器LinearLayout的高度,保证ListView的总可用高度是列表项高度(80px)的整数倍——比如要一屏显示7项,就把可用高度设为7*80=560px,不要留高度零头。
如果不需要边缘渐变效果,直接把fadingEdge相关属性关掉,避免不必要的空间占用,同时加上clipToPadding属性保证列表项可以绘制到padding区域:
<ListView android:id="@+id/nno_list" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@null" android:dividerHeight="0dp" android:fadeScrollbars="false" android:scrollbarSize="2dp" android:focusable="true" android:fadingEdge="none" android:requiresFadingEdge="none" android:clipToPadding="false" android:focusableInTouchMode="true" />
如果业务要求必须保留底部渐变效果,就给ListView设置和渐变高度一致的paddingBottom,保持clipToPadding="false",这样最后一项滚动到底部时会滑到padding区域,不会被渐变遮罩挡住。
2. 修正父布局属性
给id为nno_list_parent的LinearLayout加上android:orientation="vertical",删掉ListView上冗余的android:layout_weight="1"——父布局本身是固定高度,ListView宽高都设了match_parent,不需要weight属性参与计算,避免出现尺寸计算偏差:
<LinearLayout android:id="@+id/nno_list_parent" android:layout_width="1618px" android:layout_height="560px" <!-- 按实际需要显示的列表项数*80px设置 --> android:layout_marginLeft="25px" android:layout_marginTop="180px" android:orientation="vertical">
- 极端场景代码兜底
如果改完布局后,TV端(存在方向键操作基本是TV场景)焦点滚动还是有偏移,直接加个选中监听,焦点落到最后一项时主动触发滚动到底:
listView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (position == parent.getAdapter().getCount() - 1) { parent.post(() -> parent.smoothScrollToPosition(position)); } } @Override public void onNothingSelected(AdapterView<?> parent) {} });
内容的提问来源于stack exchange,提问作者asmhan
相关产品推荐
相关产品推荐

