Android ListView源码学习

             最近研究瀑布流代码,顺便学习了下ListView的源码。这里记录下。

        ListView源码里已经对ListView的性能进行了极大化的优化,这里主要用到了回收站(RecycleBin)这么个东西。

RecycleBin促进布局视图的重用,回收站有两个层级的存储:ActiveViews和ScrapViews。ActiveViews是指那些布局开始显示在屏幕上的Views,它们显示当前信息。在布局的底部,所有ActiveViews被降级为ScrapViews。ScrapViews是指那些有可能被Adapter使用以避免重复分配的view。就是说回收站维持着一个序列化的集合,这个集合里的view可能要比一个屏幕能显示的view的个数稍微多些。当listview滑动时,一些不可见的ActiveViews将会被降级到ScrapViews。

       下面将一条重要的调用路线记录下。onLayout------->layoutChildren---------->fillFromTop---------->fillDown------->

makeAndAddView------->obtainView.反过来记录下每个函数的具体细节。

        obtainView.这个方法是用来获取一个view然后让它带着数据在特定的位置显示。当我们发现这个view在回收站不可用时我们调用这个方法,这时候我们唯一的选择就是转换一个旧的view或者创建一个新的view。贴上代码如下。

View obtainView(int position, boolean[] isScrap) {
		isScrap[0] = false;
		View scrapView;

		scrapView = mRecycler.getScrapView(position);

		View child;
		if (scrapView != null) {
			if (ViewDebug.TRACE_RECYCLER) {
				ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP,
						position, -1);
			}

			child = mAdapter.getView(position, scrapView, this);

			if (ViewDebug.TRACE_RECYCLER) {
				ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
						position, getChildCount());
			}

			if (child != scrapView) {
				mRecycler.addScrapView(scrapView);
				if (mCacheColorHint != 0) {
					child.setDrawingCacheBackgroundColor(mCacheColorHint);
				}
				if (ViewDebug.TRACE_RECYCLER) {
					ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
							position, -1);
				}
			} else {
				isScrap[0] = true;
				//child.dispatchFinishTemporaryDetach();
				dispatchFinishTemporaryDetach(child);
			}
		} else {
			child = mAdapter.getView(position, null, this);
			if (mCacheColorHint != 0) {
				child.setDrawingCacheBackgroundColor(mCacheColorHint);
			}
			if (ViewDebug.TRACE_RECYCLER) {
				ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW,
						position, getChildCount());
			}
		}

		return child;
	}
第一次走肯定会走到else条件里去。所以child = mAdapter.getView(position, null, this);所以在我们的Adapter里convertView传入的是null,这时候需要我们的adapter inflate 我们的listview  item layout。

scrapView不为null时,就会复用view而不会再创建,从而优化了listview。在crapView = mRecycler.getScrapView(position);我们追踪getScrapView的代码发现,ScrapView其实是无序的,如果listview

的type只有一种的话,这个position是没有任何意义的。看下代码。

View getScrapView(int position) {
			ArrayList<View> scrapViews;
			if (mViewTypeCount == 1) {
				scrapViews = mCurrentScrap;
				int size = scrapViews.size();
				if (size > 0) {
					return scrapViews.remove(size - 1);
				} else {
					return null;
				}
			} else {
				int whichScrap = mAdapter.getItemViewType(position);
				if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
					scrapViews = mScrapViews[whichScrap];
					int size = scrapViews.size();
					if (size > 0) {
						return scrapViews.remove(size - 1);
					}
				}
			}
			return null;
		}
scrapViews 什么时候被初始化或者赋值的哪?在layoutChildren里执行的:recycleBin.scrapActiveViews();
这个方法将activeview降级为scrapview。

         makeAndAddView 取得View并把它添加到我们的子视图列表里。这个View可以是新的,或者是从未使用的View转换以及从回收站拿过来的。

private View makeAndAddView(int position, int childrenBottomOrTop, boolean flow,
			boolean selected) {
		View child;

		int childrenLeft;
		if (!mDataChanged) {
			// Try to use an exsiting view for this position
			child = mRecycler.getActiveView(position);
			if (child != null) {

				if (ViewDebug.TRACE_RECYCLER) {
					ViewDebug.trace(child, ViewDebug.RecyclerTraceType.RECYCLE_FROM_ACTIVE_HEAP,
							position, getChildCount());
				}

				
				// Found it -- we're using an existing child
				// This just needs to be positioned
				childrenLeft = getItemLeft(position);
				setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, true);
				return child;
			}
		}

		//Notify new item is added to view.
		
		onItemAddedToList( position, flow );
		//获取开始绘制时距离左边屏幕边框的距离
		childrenLeft = getItemLeft( position );

		// Make a new view for this position, or convert an unused view if possible
		child = obtainView(position, mIsScrap);

		// This needs to be positioned and measured
		setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, mIsScrap[0]);

		return child;
	}

如果数据没有发生改变,我们直接从回收站的ActiveView里拿出那个位置的View,获取这个View的左边距,然后

setupChild(child, position, childrenBottomOrTop, flow, childrenLeft, selected, true);
如果数据是新添加或者发生变化,我们需要调用obtainView来获取child。

setupChild 这个方法主要是确保这个子View被测定并且处于合适的位置。







你可能感兴趣的:(Android ListView源码学习)