ListView

来源:互联网 发布:阿里 知乎 编辑:程序博客网 时间:2024/05/29 10:55

相信大家在开发过程中,肯定用过ListView,那么关于ListView的面试题有哪些呢,我们一起来看看。

一、什么是ListView

当面试官问你什么是ListView,你的脑海中是不是只有图像没有语言,所以给大家讲一下什么是ListView。
ListView就是可以把数据集合以滚动的方式展示到屏幕上的一个view。
两个关键点:一个数据集合,一个滚动的方式。

二、 ListView适配器模式

提到ListView,大家肯定会想到Adapter,Adapter叫做适配器,它会给每一条数据源制作一个View,然后交由ListView来展示,所以适配器就像是数据源和ListView之间的桥梁一样,把两者联系在一起。
这里写图片描述

三、ListView中的RecycleBin机制

那大家有没有想过为什么ListView就可以实现滑动的效果呢?先给大家放一张图。

这里写图片描述

上面这张图就很好的解释了为什么我们的ListView可以实现滑动的效果。原来我们的ListView中的每一个元素就是一个Item,当这个Item滑出屏幕的时候,它会被添加到一个叫RecycleBin的东西里面,而新进入屏幕的元素则会从RecycleBin里面取出一个Item然后装载数据显示到ListView上,这样就实现了一个循环,只要有数据源就可以不停的往RecycleBin中加入Item,取出Item。

那说了这么久的RecycleBin,它到底是个啥嘞?

点开它的源码我们可以看到,它当中有很多的数组变量,我们当然不会关注那么多,大家根据上面的图想一想,我们是不是只要知道三个变量就行了,第一个就是当前ListView中能看到的所有元素,第二个就是所有已经被加入到RecycleBin中的元素,第三个就是当前正要准备加入到RecycleBin中的元素。

private View[] mActiveViews = new View[0];  //存储的是活动的Views,即显示在屏幕上的Views,可以被直接复用private ArrayList<View>[] mScrapViews;//二维数组,表示所有被废弃类型的View的集合private ArrayList<View> mCurrentScrap;//表示当前被废弃的View的集合

当然了,ListView中的数据有很多种类型,比如纯文字的,文字加一张图的,文字加两张图的等等,每一种数据源只能复用相同的Item,否则就会出现排版错乱。所以RecycleBin通过下面这个方法为我们每一种数据类型建立了一个RecycleBin机制,让后面的元素选择。也就是说RecycleBin只有一个,但是它里面有很多的集合,每一个集合就是一个数据类型。

//为ListView中的每一个类型的数据项建立一个recycleBin机制//默认为1,只有一种数据类型public void setViewTypeCount(int viewTypeCount) {    if (viewTypeCount < 1) {    throw new IllegalArgumentException("Can't have a viewTypeCount < 1");}//noinspection unchecked    ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];    for (int i = 0; i < viewTypeCount; i++) {    scrapViews[i] = new ArrayList<View>();  //创建viewTypeCount个集合}    mViewTypeCount = viewTypeCount;    mCurrentScrap = scrapViews[0];    mScrapViews = scrapViews;}

滑出屏幕的Item通过下面这个方法加入到RecycleBin中:

//把当前要废弃的View添加到ScrapView数组当中void addScrapView(View scrap, int position) {final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();if (lp == null) {// Can't recycle, but we don't know anything about the view.// Ignore it completely.return;}lp.scrappedFromPosition = position;// Remove but don't scrap header or footer views, or views that// should otherwise not be recycled.final int viewType = lp.viewType;if (!shouldRecycleViewType(viewType)) {// Can't recycle. If it's not a header or footer, which have// special handling and should be ignored, then skip the scrap// heap and we'll fully detach the view later.if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {getSkippedScrap().add(scrap);}return;}

新滑入的Item想要显示就要从RecycleBin中去取。

/*** @param childCount  要存储的View的数量* @param firstActivePosition  ListView中第一个可见元素的position值*/void fillActiveViews(int childCount, int firstActivePosition) {    if (mActiveViews.length < childCount) {    mActiveViews = new View[childCount];}    mFirstActivePosition = firstActivePosition;    //noinspection MismatchedReadAndWriteOfArray    final View[] activeViews = mActiveViews;    for (int i = 0; i < childCount; i++) {    View child = getChildAt(i);    AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();    // Don't put header or footer views into the scrap heap    if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {    // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.    // However, we will NOT place them into scrap views.    activeViews[i] = child;    // Remember the position so that setupChild() doesn't reset state.    lp.scrappedFromPosition = firstActivePosition + i;    }   }}
/**      和fillActiveViews配套使用,用于获取屏幕上显示的View* @param position The position to look up in mActiveViews* @return The view if it is found, null otherwise*/View getActiveView(int position) {    int index = position - mFirstActivePosition;    final View[] activeViews = mActiveViews;    if (index >=0 && index < activeViews.length) {    final View match = activeViews[index];  //将可见元素的position值作为可见view集合的角标使用    activeViews[index] = null;  //设为null,所以屏幕上的View不能被重复利用    return match;   }    return null;}

通过这两个方法,就可以让新滑入的元素显示到屏幕上。

好了,关于ListView的源码就分析到这里了,主要就是三个变量,一个是当前屏幕上显示的所有元素的集合,一个就是所以已经被废弃的元素的集合,还要一个就是当前正要废弃的被废弃的元素。然后就是把要废弃的元素加入到RecycleBin中,新滑入的元素从RecycleBin中取出并显示到ListView中。但是有一点要注意就是,只有被加入到RecycleBin中的Item才可以复用,正在屏幕上显示的Item是不可以复用的。

四、ListView的优化

1、Item显示优化

我们都知道ListView每一次要显示一个新元素的时候都要销毁旧元素,再创建新元素,频繁的销毁创建会导致ListView卡顿,也会让整个界面显得不流畅,所以针对ListView,我们可以进行一些优化,来让它不那么频繁的销毁创建,而是复用之前的Item。

这里写图片描述

使用convertView和ViewHolder来配合。
每当有view移出界面的时候,它就会变成convertView,所以,在调用getView()方法时,我们先判断一下convertView是否为空,如果为空的话,我们就新创建一个布局和viewHolder,并使用viewHolder来绑定convertView中的控件,并且把viewHolder保存在convertView中。如果convertView不为空,那么就直接取出其中保存的viewHolder,再进行其他的操作。viewHolder是一个内部类,它里面保存了ListView中所有控件的信息,减少了findViewById的次数。

这样我们就完成了item显示的优化。我们知道ListView中很多时候都要显示图片,所以针对图片,我们也可以进行一些优化。

2、图片显示优化

可以用三级缓存来展示图片;在geiView方法中做耗时操作会导致界面的卡顿,所以我们可以为ListView设置一个滑动监听,界面滑动的时候我们不去加载图片,界面停止的时候我们才去加载图片。

3、硬件加速

开启硬件加速,也可以提高ListView展示的效率。

另外还有一些其他的方法,比如设置半透明元素等等,这里就不多说了,主要的方法就是上面第一点和第二点。

总结

好了,总结一下吧,ListView就是一个可以将数据集合以滚动的方式展示到屏幕上的view。每一个Item都是通过Adapter来创建View的,Adapter就像是数据源和ListView之间的桥梁。它实现滚动的方式,是因为它的内部有一个RecycleBin,可以存放滑出屏幕的元素的Item,需要显示的元素从RecycleBin中取出Item并加载自己的数据来让ListView显示。针对ListView我们还做了很多的优化来提高性能,比如复用convertView和内部类viewHolder,比如图片的三级缓存,比如设置滑动监听等等。

好了,继续学习喽~~~
这里写图片描述

原创粉丝点击