阿里干货课堂丨Android 之 Listview

LisView显示的三要素

1:  ListView,用来展示列表   

2:Adapter,用来存在数据&将数据与视图进行绑定

3:数据,将被映射的字符串、图片url等等。


关于Adapter ,请参考 Android 之 Adapter。

ListView的工作原理

Adapter的作用就是ListView界面与数据交互的桥梁,当列表里面每一项显示到页面上时,都会调用Adapter的getView方法。 系统需要回执ListView时,首先会调用getCount()函数,得到要绘制的这个列表额长度,然后开始从第一行开始绘制,每行的回执方法是调用getView函数。那么Android是不是为每一行都会新创建一个View呢?试想加入行数为几万行,内存肯定会爆掉的,所以Android官方早就想到了这一点,在ListView实现中添加了视图的缓存-Recycler,每当有行移除屏幕的可视区域时,这个被移除的行的View对象就会被添加到Recycler中,也就是在渲染新行时的那个参数convertView。

LisView的初使化

疑问一:

ListView绘制时是如何获取每行的View的呢?


首先ListView通过setAdapter方法,将Adapter与ListView关联起来,查看ListView的setAdater方法源码可知,setAdater将传递经来的Adapter的引用复制给了内部全局变量mAdapter,ListView在绘制每行的时候根据行号position调用父类AbsListView中的obtainView,obtainView首先会从recycler中获取是否有匹配的视图,如果存在的话,可以看到则调用adapter.getView方法,并传递了scrapView给convertView变量,否则传递的是null。


疑问二:

ListView中数据发生变更了,我们一般会调用Adapter的 notifyDataSetChanged()方法, 那么视图是怎么发生变化的呢?


ListView 中的数据适配器Adapter采用的是观察者模式(参考之前的”观察者模式“)ListView在setAdapter时,会新建ApdateData SetObserver,并注册此观察者。AdapterDataSetObserver类实在AbsListView中定义:AdapterView中AdapterDataSetObserver的实现如下(部分省略):AdapterDataSetObserver 实现了DataSetObserver接口,并重写了onChanger方法,里面调用了requestLayout方法,此方法的作用是要求parent view 重新调用它的哦弄Measure onLayout方法重新布局视图,但不会重新绘制任何视图包括该调用者本身。 requestLayout的实现方法需要到View类中查看:

可参考:

http://blog.csdn.net/androiddevelop/article/details/8561076


疑问三:

RecyclerBin的数据结构是这样的呢?当ListView有多个视图类型(在界面上就是有不同的样式和数据类型)又是怎么选择合适的convertView的呢? 首先看一下RecycleBin的类定义(AbsListView中内部类)


从注释中我们就可以得知 :


RecycleBin一共有两个存储结构分别是ActiveViews 和 ScrapViews ActiveViews储存当前在界面(手机显示区域)中显示View,移出界面会存入ScrapViews ScrapViews存储当前已经滑动出当前界面(手机显示区域)显示的View,这些view存储起来相当于回收,当再次请求的时候从此存储中取出反复使用。当ListView中有N个视图类型时,RecycleBin会创建N个scrapView数组,每个类型一view数组,后面在获取view时会先判断view的类型,然后到对应的数组中去取。怎么从ScrapViews中获取可用的view视图呢?getScrapView → retrieveFromScrap retrieveFromScrap(这个不属于RecycleBin类,是属于外部类AbslistView中的方法) 根据position,从mScrapView中找:


1. 如果有view.scrappedFromPosition = position的,直接返回该view;

2. 否则返回mScrapView中最后一个;

3. 如果缓存中没有view,则返回null;



下面,我们来分析下这三种情况在什么条件下满足?


a. 第三种情况,这个最简单:

一开始,listview稳定后,显示N个,此时mScrapView中是没有缓存view的,当我们向上滚动一小段距离(第一个此时仍显示部分),新的view将会显示,此时listview会调用Adapter.getView,但是缓存中没有,因此convertView是null,所以,我们得分配一块内存来创建新的convertView;



b. 第二种情况:

在a中,我们继续向上滚动,直接第一个view完全移出屏幕(假设没有新的item),此时,第一个view就会被detach,并被加入到mScrapView中;然后,我们还继续向上滚动,直接后面又将要显示新的item view时,此时,系统会从mScrapView中找position对应的View,显然,是找不到的,则将从mScrapView中,取最后一个缓存的view传递给convertView;

   

c. 第一种情况:

紧接着在b中(标示为橙色的文字后面),第一个被完全移出,加入到mScrapView中,且没有新增的item到listview中,此时,缓存中就只有第一个view;然后,我此时向下滑动,则之前的第一个item,将被显示出来,此时,从缓存中查找position对应的view有没有,当然,肯定是找到了,就直接返回了。


ListView使用过程中遇到的问题

Listview 滑动中背景错乱:

场景:

我有一个listView作为菜单,当菜单选中时有一个指示器会展示出来,其他的会被隐藏,默认第一个菜单为选中状态,但是当滑动listview时,新加在出来的item中指示器也展示出来了。


原因:

理解了上面的getView和RecycleBin的工作原理之后,这个问题不难定位,肯定是在渲染新行时从scropViews中没有匹配position位

置的view,此时默认返回了最后一个view(也就是场景中对应的第一个),这个view是被我们设置了指示器为可见状态,所以就引起了背景的错乱。


解决办法:

对于positon等于我们选择的下标时会指示器可见,当position不等于我们选择的下标时,防止convert被复用,我们需要设置指示器为不可见,也就是else中的代码,之前我只写了if中的代码,却忽略了else中的代码。


这个问题也可以参考:

http://www.myexception.cn/mobile/1612364.html


更多文章请关注订yue号:java_xuetang


猜你喜欢

转载自blog.csdn.net/bryans/article/details/80800683
今日推荐