一天一Android之ListView
说起ListView,就不得不说iOS的UITableView,毫不夸张的说的,放在3年前,如果你去面试的时候,你说你会用UITableView,知道UITableView的代理方法,不用说了,你可以直接来上班了。
ListView在Android中开发的重要性不言而喻,学好ListView,我想在以后的列表开发中就不用发愁了。什么是列表开发?这么说吧,在你使用的APP中,80%的都会用到列表开发,比如微信的聊天页,QQ的个人空间页。为何要用列表开发,列表开发的优越在哪里?我今天来一探究竟。
ListView初体验
我们先来写个小例子,根据这个小例子我们再做进一步的介绍。我们先在xml中创建一个ListView,这里我直接使用ConstraintLayout约束布局。
| 
 | 
MainActivity的代码:
| 
 | 
运行如下:

ListView
在Android所有常用的原生控件当中,用法最复杂的应该就是ListView了,它专门用于处理那种内容元素很多,手机屏幕无法展示出所有内容的情况。ListView可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移动到屏幕内了。
我们先看看ListView的继承体系:
属性
- android:divider在列表条目之间显示的图片或者颜色(drawable或color)
- android:dividerHeight用来指定divider的高度
- android:scrollbars设置滚动条状态,不需要滚动条时,设置为none
- android:listSelector设置条目选中后的颜色,可设置为#00000000或者@android:color/transparent 取消选中色
- android:footerDividersEnabled当设置为false时,ListView将不会在各个footer之间绘制divider,默认为true
- android:headerDividersEnabled当设为false时,ListView将不会在各个header之间绘制divider,默认为true
其他继承父类的属性就不说了。
方法
- void addFooterView(View v)添加一个固定在列表底部的View
- boolean removeFooterView(View v)删除一个之前添加的FooterView,参数为欲删除的视图,返回是否删除成功
- void addHeaderView(View v)添加一个固定在列表顶部的View
- boolean removeHeaderView(View v)删除一个之前添加的HeaderView,参数为欲删除的视图,返回是否删除成功
- void setAdapter(ListAdapter adapter)为ListView绑定Adapter
- ListAdapter getAdapter()返回ListView正在使用的Adapter
- void setEmptyView(View emptyView)当数据的个数为0的时候显示一个提示视图
通过上面一个简单的例子我们可以看出,ListView如果想要显示数据,必须需要一个Adapter来适配。Android为什么这么设计呢?如果学习过iOS的同学都知道,我们在使用UITableView的时候,一定会实现它的数据源代理方法,在代理方法中我们会返回条目数和条目View。这种设计方法有效的分离了UITableView和数据源的直接打交道,让数据源的显示交于用户来选择。Android也是如此。
ListView只承担交互和展示工作,至于这些数据来源于哪里,ListView并不关心。于是就有了Adapter这样一个机制的出现。Adapter是适配器的意思,它在ListView和数据源之间起到了一个桥梁的作用,ListView会借助Adapter这个桥梁去访问真正的数据源,因为Adapter的接口都是统一的,因此我们可以通过实现接口来定制各种类型的Adapter。另外系统也为我们实现了一些常用的Adapter,比如我们上面用到的ArrayAdapter等。
Adapter
我们先来看看继承体系:
Adapter定义的抽象函数主要包括:
- void registerDataSetObserver(DataSetObserver observer)添加数据源变化的observer,如增加、删除等将会执行
- void unregisterDataSetObserver(DataSetObserver observer)取消注册的observer
- int getCount()显示有多少个数据项 即adapter有多少个条目
- Object getItem(int position)返回数据集中position位置所对应的数据项
- long getItemId(int position)返回position位置所对应的ID号,通常即为position
- View getView(int position, View convertView, ViewGroup parent)核心函数,返回position数据项对应的条目View
上个示例我们使用的ArrayAdapter,他只能用来显示TextView,如果我们想显示更多的不同种类的条目,我们需要继承BaseAdapter,并重写相关方法,我们现在来看看如何重写。
先上示例图:

我们新建一个Dog类,有name和imageId两个成员变量,分别表示狗的名字和图片资源(这里使用本地图片):
| 
 | 
我们在新建一个DogAdapter继承于ArrayAdapter
| 
 | 
注意:我们都知道ListView的强大,它强大就强大在无论我们设置多少条数据源,ListView都不会完全的把这些条目都创建,而是通过复用已经消失在屏幕的条目来展示新的条目。
在getView函数中,有个convertView参数,如果它不为空,就表示ListView的缓存池中有可复用的条目,我们直接取来用就行。而且我们还创建了一个内部类ViewHolder,声明了两个属性mImageView和mTextView,我们可以给View设置tag,方便下次给View赋值的时候,不需要再次调用findViewById方法来重新查找属性。
再来看我们如何使用DogAdapter:
| 
 | 
其他方法
触摸监听 OnTouchListener
| 
 | 
滑动监听 OnScrollListener
| 
 | 
条目点击 OnItemClickListener
| 
 | 
仿微信聊天界面
说了这么多,再来做一个例子,仿一下微信的聊天界面。
先看布局,这里依旧使用约束布局:
ListView布局
| 
 | 
item布局
这里我们把两种布局都定义在一个xml文件中,稍后会根据代码来决定隐藏哪种类型。
| 
 | 
消息实体 MSG类
我们再创建一个消息实体,来存储消息信息。这里我们定义了两种消息类型,TYPE_RECEIVED表示接收的消息,TYPE_SENT表示发送的消息。
| 
 | 
消息适配器 WXMsgAdapter
重点来了,我们会根据消息的类型来决定显示哪种布局方式。
| 
 | 
我们在WXChatActivity中这样用:
| 
 | 
运行结果如下:

小结
写完微信的小例子,心里的成就感还是很强的,ListView的强大还远不止如此,因为我知道iOS的UITableView的重要性,相信在以后的开发中我会经常和ListView打交道的。
