Android ListView 常用技巧总结

本文对 ListView 中的一些常用技巧做一个总结。附:虽然现在 RecyclerView 已逐渐取代 ListView,但实际情况是大部分项目中还在使用 ListView。当然,后续我会在我的博客中详细介绍 RecyclerView,敬请期待。

前言

ListView 作为 Android 中常用的列表控件,用以向用户展示列表信息。可以说,我们开发的项目中 listView 随处可见,也是 Android 程序员面试时,最常被问的一个知识点,下面我就将 listView 中的常用技巧一一罗列出来。

listView 的缓存机制以及 ViewHolder 的使用

这两个知识点是最常用的优化 listView 的技巧。

1.首先,来说说 listView 的缓存机制,再这之前,我们先看看下面这张图:

Android ListView 常用技巧总结

我先来说一下这张图的意思,从左边第一张图开始,当我们的手机上显示一个 listView 列表的时候,如图有 7 个 item,当我们向上拖动 listView,进入第二个示意图,注意细节,第二个图例表示的是,当向上拖动时,这是 item00 正准备消失在手机屏幕上但还没有完全消失时,item07 出现在我们的视野内,这时,item07 也只是显示一半,这种情况下,item07 还不是从缓存中复用的 item,而是重新 new 的一个 View.我们继续向上拖动,到了第三个图例,这时 item00 已经完全消失在屏幕上,这时系统不会销毁这 item00,而是将其放到了缓存中,那么当我们继续向上滑动时,我们就可以利用 listView 的 缓存机制,复用缓存中 item,用它显示 item08,而不是再次的重新 new 一个.

2.通过 缓存机制 和 ViewHolder 配合使用

知道了 缓存机制,我们还需要知道 ViewHolder,这时 Google 大会上推荐我们使用的 listView 优化方案。

我们知道,我们通常写的视图 xml 文件,实际上是一个 DOM 树结构,而 findViewById() 实际上是遍历整个视图树,当你的 listView 中的 item 非常复杂时,findViewById() 所需的时间就越长,这是非常耗性能的。

1
2
3
4
5
6
static class  {

TextView title;
ImageView img;

}

我们可以在第一次创建 listView 中的 item 的同时,创建一个 ViewHolder,将需要遍历的视图保存在其中,通过 View.setTag() 保存在缓存中,这样下次重用的时候就避免的再次 findViewById() 的操作了。

注意:这样做虽然消耗了一些内存,但是与 findViewById() 遍历所需的时间相比,显得微不足道。(据测试,使用 ViewHolder,效率提高 50%)

3.示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
// 判断是否存在缓存
if (convertView == null) {
convertView = this.mInflater.inflate(R.layout.item_lv_weixn_about, null);

viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
// 若有,取出缓存中存放的 ViewHolder,避免再次 findViewById().
viewHolder = (ViewHolder) convertView.getTag();
}

// 更新视图
viewHolder.title.setText(mStrArr[position]);

return convertView;
}

设置 listView 中的分割线

通过 android:dividerandroid:dividerHeight 这两个属性,我们可以设置设置分割线。另外,除了为分割线设置颜色以外,我们还可以为其设置图片资源:

1
2
android:divider="@drawable:split_pic"
android:dividerHeight="1dp"

隐藏 listView 的滚动条

我们在拖动 listView 时,默认情况下是有滚动条的,我们可以通过设置 scrollbars 属性来控制滚动条的状态。

1
android:scrollbars="none"

设置为 none,则不会出现滚动条。

取消 listView 的 item 的点击效果

listView 默认下,但我们点击其中的 item 时,会有点击效果。通过设置 listSelector 属性来取消点击效果。

1
android:listSelector="#00000000"

你还可以使用 Android 自带的透明色来实现这个效果:

1
android:listSelector="@android:color/transparent"

设置 listView 需要显示在其中某一项

listView 默认情况下是显示在第一项的,有时候,需求是显示在指定项,那么要如何实现呢?

代码控制:

1
listView.setSelection(position)

通过制定 position,来设置需要显示在第几项。但是,这种滑动操作是瞬间完成的,有时候,我们的需求是 平滑 的滑动到某个位置,通过下面的代码可以实现这种效果:

1
2
3
mListView.smoothScrollBy(distance, duration);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollToPosition(index);

动态修改 listView

listView 中的数据通常情况下,在做了某种操作后,需要动态更新,如何实现这种功能呢

通过更新 listView 中的数据源,一般情况下都是一个 List<Object>,我们通过向 list 中添加数据,然后调用 mAdapter.notifyDataSetChanged() 方法,就能实现 listView 的动态更新了。

1
2
mData.add("info");
mAdapter.notifyDataSetChanged();

遍历 listView 中的 item

通过 getChildAt() 来获取某个 View:

1
2
3
for (int i = 0; i< mListView.getChildCount(); i++=) {
View view = mListView.getChildAt(i);
}

交互,当 listView 为空时

当我们 listView 加载数据为空时,列表就是空白一片,这一点对于用户体验是不好的,应该给用户以提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android=大专栏  Android ListView 常用技巧总结">"http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>


<ImageView
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/empty_image"
/>

</FrameLayout>

这里,我们使用一张 数据为空 的图片,当 listView 为空时,就显示这张 数据为空 的图片,通过以下代码可以达到这种效果:

1
2
ListView listView = (ListView) findViewById(R.id.listview);
listView.setEmptyView(findViewById(R.id.empty_view));

listView 滑动监听

滑动监听是 listView 非常重要的技巧,我们通常需要监听不同的事件,来做不同的逻辑处理。通常情况下,我们还需要借助 GestureDetector 手势识别,VelocityTracker 滑动速度监测等。比如说,下拉刷新,快速滑动不异步加载图片等等。

下面我们介绍两种 listView 滑动事件的方法:

1.OnTouchListener

OnTouchListener 是 View 中的监听事件,我们可以用来监听 ACTION_DOWN, ACTION_MOVE, ACTION_UP 这三个事件发生的坐标,从而得知用户滑动的方向,做相应的逻辑处理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mListView.setOnTouchListener(new View.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 触摸时,调用这里
break;
case MotionEvent.ACTION_MOVE:
// 移动时,调用这里
break;
case MotionEvent.ACTION_UP:
// 手指离开触摸屏时,调用这里
break;

}
return false;
}
});

2.OnScrollListener

OnScrollListener 是 AbsListView 中定义的监听事件,它封装了很多与 ListView 相关的信息。我们先看看 OnScrollListener 的一般使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {

public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
// 滑动停止时,调用这里
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
// 正在滚动时,调用这里
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
// 手指猛地滑动调用,由于惯性,在手指离开后,还会继续滑动一段距离
break;
}
}


public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 滚动时会一直调用这里
}
});

看上面的代码,我们知道 OnScrollListener 中定义了两个回调方法,分别是:

  • 1.onScrollStateChanged()
  • 2.onScroll()

先说说 onScrollStateChanged(),我们可以看到参数 scrollState 对应三种模式:

  • 1.OnScrollListener.SCROLL_STATE_IDLE : 滚动停止时;
  • 2.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL : 正在滚动时;
  • 3.OnScrollListener.SCROLL_STATE_FLING : 用力滑动。

注意,但我们没有用力滑动时,这个方法只会调用 2 次,否则调用 3 次,差别就是 OnScrollListener.SCROLL_STATE_FLING 这个模式。

再来看看 onScroll() 这个回调,它会在 listView 滚动时一直调用,方法中的 3 个参数准确的显示了当前 listView 的滚动的状态:

  • 1.firstVisibleItem : 当前能够看见的第一个 item 的 ID;
  • 2.visibleItemCount : 当前能看见的 item 的总数;
  • 3.totalItemCount : 整个 listView 中 item 的总数;

通过这几个参数,我们能很方便的判断出是否滚动到了最后一行,代码如下:

1
2
3
if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {
// 滑动到了最后一行
}

我们还可以通过代码来判断滚动的方向:

1
2
3
4
5
6
if (firstVisibleItem > lastVisibleItemPosition) {
// 上滑
} else if (firstVisibleItem < lastVisibleItemPosition) {
// 下滑
}
lastVisibleItemPosition = firstVisibleItem; // 记录上次滑动时,第一个 item 的 ID

另外,listView 还提供了一些封装的方法来获取当前可视的 item 的相关信息:

1
2
3
4
// 获取屏幕区域内的第一个 item 的 id
mListView.getFirstVisiblePosition();
// 获取屏幕区域内的最后一个 item 的 id
mListView.getLastVisiblePosition();

本文参考:

Android ListView 常用技巧总结

上一篇:彻底理解Android Binder通信架构


下一篇:二、Android XML数据解析