“纸上得来终觉浅,绝知此事要躬行。”以前虽然经常听说Adapter有复用问题,但是没有自己遇到过之前,只是有个印象,但到底是什么问题?不知道!直到后来,一个ListView中需要展示两种布局,按照习惯哐哐哐写下去,结果后面一运行,咦~这都什么鬼?两种布局乱七八糟,本该是第一种布局在的地方,偶尔会冒出第二种布局,本该第二种布局在的地方,有时却是第一种布局。两种布局就像捣蛋的熊孩子,不听指挥。这时,才知道Adapter服用是什么问题。
一、ListView和Adapter的简单使用
Android应用中使用Adapter的地方很多,ListView的使用也很简单,使用ArrayAdapter、SimpleAdapter等内置Adapter就不多说,我们使用自由度最高的BaseAdapter,加载自己的布局。简单使用的代码如下:
1.在xml中定义ListView.
2.定义Adapter.
3.列表每项的视图布局
4.封装数据的model
5.最后在Activity或者Fragment中使用LIstView
这样,一个简单的ListView就完成,效果如下:
二、复用itemView,优化ListView
实际上ListView有复用机制,第一次只加载能铺满屏幕的itemView,当ListView上划或下拉,从屏幕上消失的itemVIew会进入Recycler,而即将显示的子项需要新的itemView时,会去Recycler中查找,如果已有则会服用。如下图:
再看我们Adapter,每次显示都会加载一次布局,但当item9显示在屏幕上的时候,实际上布局已经加载好了,我们在加载一次,造成了很大的资源浪费。因此,可以判断itemView(即convertView)是否已加载,如果已加载,则不必重新加载,只要设置改变属性就好。修改后的Adapter如下:
如图,用一个ViewHolder把View保存起来,下次使用itemView的时候,判断它是空的才会去加载布局,否则就从ViewHolder中吧View取出来,改变其属性,便可得到一个新的itemView!
三、复用Adapter遇到的坑
大部分Android应用的中ListView都是用以上方法实现的,而且一般情况下,列表的每一项都是一样的,这样已经够用了。但是,需求总是多种多样的,加入需要让listView的最后一项显示不同的布局,如下图。那我们该怎么办呢?
1.我们按步骤慢慢来,首先,在model中添加一个type字段,用来判断该使用哪种类型。如下图:
2.往dataList中添加一个model,并设置type为2。
3.添加最后一项的布局文件,注意,这一项没有ImageView,如下:
4.在Adapter中进行判断,将type为2的另一种布局加载进来。
OK,大功告成,在Adapter中进行了判断,如果type为2,就加载 illustration_list_item_2.xml,没毛病!我们run一下看看效果吧:
???歪?为什么前面会有ImageView?明明已经判断了啊!
分析:
还记得上一节讲的Adapter复用吗?加载最后一项的时候,回去Recycler中查找有没有多余的itemView,如果有则复用,convertView不为空,所以不会重新加载布局,只会设置属性。
既然这样,我们不做判空了,每次进入最后一项的时候都加载这个布局吧!反正只有最后一项使用了这个布局,不判空直接加载也不会造成多少浪费。Adapter代码修改如下:
拉到最后一项,看下效果:
Perfect!成功了,我果然是天才!
然后,往上拉一下……纳尼?闪退了?看下日志:
空指针?检查一下代码,原来是大意了,没有吧HoldView设置为convertView的Tag,把代码添加进去,重新运行:
OK,不闪退了!我果然是个天才……只要我不粗心。
上拉,下拉,看看效……等等,这是什么鬼?
为什么正常布局中.出了一个叛徒?
分析:
原来不光是最后一项会复用前面的布局,上拉的话前面的也会复用最后一项的布局。
这样改怎么办呢?怎么知道该再什么时候加载哪个布局呢?好吧,我承认有时候我也不那么天才。
既然不知道什么时候该加载什么布局,那我就把两种都放同一个布局里吧?代码如下:
这样的方法都让我想到了,我真是个人才!跟着我上拉、下拉一个慢动作,上拉、下拉慢动作重播……下面,见证奇迹的时刻到了!
(╯°Д°)╯︵ ┻━┻悠悠苍天,何薄于我!
哐哐哐~
外卖小哥:你的外卖到了。
我:改bug呢,没时间吃外卖。
外卖小哥:我看看,你把一个布局显示的时候,忘了隐藏另一个布局了。
我:O_o
经外卖小哥修改后,代码如下: