ListView 优化及相关问题总结

ListView 优化

最近在做项目过程中频繁使用列表,今天抽空总结下过程中遇到的问题,下面会有具体对应的解决办法;

  • 1.listView 外套一层ScrollView 的问题,就是listView 只显示一行或者两行, 我发现这个问题在 RecycleView 上也存在, 在RecycleView外套一层 ScrollView也会出现只显示一行的问题,滑动只能在 一行的高度内滑动。这问题下面有具体解决方案,说下我是怎么解决的吧,因为我要实现的是9宫格的图片展示,本来是用的RecycleView 来实现的,出现了这个问题,我就尝试把recyclerView 换成了GridView 了,因为GridView 本身就支持多行展示,发现就没有这个问题了,套了ScrollView 也正常展示;
  • 2.也是listView 在嵌套ScrollView 中的问题,在某款三星手机上,这个手机的虚拟按键,可以锁定和解锁定, 发现在切换锁定和解锁定中,会导致原来的多行的图片变成单行, 在别的手机上就没有这个问题, 这个奇怪的现象,最终还是以GridView 替换掉listView 解决的。
  • 3.listView 添加 headerView的问题, 需要注意的是 添加进去的 HeaderView 就占据了第0位,所以在使用的 onItemClick 的时候,需要-1, addfooterView的时候可以用 view.getFooterViewsCount() 是否等于0来判断,避免多次添加。
  • 4.由于listView 会复用viewHolder ,所以我们在getView 中 显示隐藏的设置一定要配套使用,if里有显示,在else 中就要 隐藏。

优化步骤:

使用 RecycleView 代替listview

  • 1.重用ConvertView;
  • 2.使用View Holder模式;
  • 3.使用异步线程加载图片(一般都是直接使用图片库加载,如Glide, Picasso);
    建议:
  • 1.在adapter的getView方法中尽可能的减少逻辑判断,特别是耗时的判断;
  • 2.避免GC
  • 3.在快速滑动时不要加载图片;
  • 4.将ListView的scrollingCache和animateCache这两个属性设置为false(默认是true);
  • 5.尽可能减少List Item的Layout层次(如可以使用RelativeLayout替换LinearLayout,或使用自定的View代替组合嵌套使用的Layout)

ListView 嵌套 ScrollView

Listview不能显示正常的条目,只显示一条或二条

这是因为:由于listView在scrollView中无法正确计算它的大小, 故只显示一行。

解决方案:

1. 方法一:重写ListView, 覆盖onMeasure()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WrapperListView.java:
public class WrapperListView extends ListView {
public WrapperListView(Context context) {
super(context);
}
public WrapperListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WrapperListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public WrapperListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* 重写该方法,达到使ListView适应ScrollView的效果
*/

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
2.方法二:动态设置listview的高度,不需要重写ListView

只需要在setAdapter之后调用如下方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void setListViewHeightBasedOnChildren(ListView listView) {
// 获取ListView对应的Adapter
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0, len = listAdapter.getCount(); i < len; i++) {
// listAdapter.getCount()返回数据项的数目
View listItem = listAdapter.getView(i, null, listView);
// 计算子项View 的宽高
listItem.measure(0, 0);
// 统计所有子项的总高度
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
// listView.getDividerHeight()获取子项间分隔符占用的高度
// params.height最后得到整个ListView完整显示需要的高度
listView.setLayoutParams(params);
}

这时最好给ListView之外嵌套一层LinearLayout,不然有时候这种方法会失效

3.方法三:在xml文件中,直接将Listview的高度写死

可以确定的是:这种方式可以改变ListView的高度,但是,还有一个严重的问题就是listview的数据是可变动的,除非你能正确的写出listview的高度,否则这种方式就是个鸡肋。

4. addHeadView()

如果只有数据 layout 完全没有必要嵌套 ScrollView 的.之说以需要嵌套,很多是在listView 头部或者底部多了一部分Layout ,这个时候可以尝试 listView 的 addHeadView() 尝试解决

在一次显示ListView的界面时,getView会被执行几次?

比如有5组数据要填充到listView。listView会先调用onMeasure,此时会调用5次getView。然后才调用onLayout,此时又会调用5次getView,这样就重复了。所以导致多次调用getView方法

的确调用了三遍

解决办法:

ListView 的高度 从 wrap_content 改成 match_patent 或者一个固定值,就能减少getview 调用次数

或者重写 listView 的onMasure onLayout 方法 赋值 一个变量 检测 是测量还是layout 测量时不加载自己写的那段逻辑

listview失去焦点怎么处理?

在listview子布局里面写,可以解决焦点失去的问题
android:descendantFocusability=”blocksDescendants”

ListView 优化

  • 1.首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一个回收器,Item 滑出界面的时候 View 会回收到这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。

  • 2.利用好 View Type,例如你的 ListView 中有几个类型的 Item,需要给每个类型创建不同的 View,这样有利于 ListView 的回收,当然类型不能太多

  • 3.尽量让 ItemView 的 Layout 层次结构简单,这是所有 Layout 都必须遵循的
  • 4.善用自定义 View,自定义 View 可以有效的减小 Layout 的层级,而且对绘制过程可以很好的控制;
  • 5.尽量能保证 Adapter 的 hasStableIds() 返回 true,这样在 notifyDataSetChanged() 的时候,如果 id 不变,ListView 将不会重新绘制这个 View,达到优化的目的;
  • 6.每个 Item 不能太高,特别是不要超过屏幕的高度,可以参考 Facebook 的优化方法,把特别复杂的 Item 分解成若干小的 Item,
  • 7.为了保证 ListView 滑动的流畅性,getView() 中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载,这个库可以帮助你 Glide:https://github.com/bumptech/glide
  • 8.使用 RecycleView 代替。 ListView 每次更新数据都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推荐使用。
请联系我!