1 onLayout方法
- ListView继承了AbsListView,AbsListView是ViewGroup的子类;自定义控件继承了ViewGroup的话,会重写onLayout方法
- ListView没有重写onLayout方法,但是其父类重写了
ABSListView.onLayout
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
final int childCount = getChildCount();
if (changed) {
for (int i = 0; i < childCount; i++) {
getChildAt(i).forceLayout();
}
mRecycler.markChildrenDirty();
}
layoutChildren();
mInLayout = false;
mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
// TODO: Move somewhere sane. This doesn't belong in onLayout().
if (mFastScroll != null) {
mFastScroll.onItemCountChanged(getChildCount(), mItemCount);
}
}
- AbsListView 的onLayout方法中,比较关键的就是调用了layoutChildren方法,此方法在ListView中具体实现了
2 ListView的layoutChildren()方法
- 在layoutChildren中会有一个mLayoutMode的switch判断,走默认代码块调用fillFromTop方法
- fillFromTop调用fillDown方法 ,fillDown中会通过makeAndAddView获取每一个child
private View fillDown(int pos, int nextTop) {
View selectedView = null;
int end = (mBottom - mTop);
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
end -= mListPadding.bottom;
}
while (nextTop < end && pos < mItemCount) {
// is this the selected item?
boolean selected = pos == mSelectedPosition;
View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
nextTop = child.getBottom() + mDividerHeight;
if (selected) {
selectedView = child;
}
pos++;
}
setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
}
3 makeAndAddView方法
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
if (!mDataChanged) {
// Try to use an existing view for this position.
final View activeView = mRecycler.getActiveView(position);
if (activeView != null) {
// Found it. We're reusing an existing child, so it just needs
// to be positioned like a scrap view.
setupChild(activeView, position, y, flow, childrenLeft, selected, true);
return activeView;
}
}
// Make a new view for this position, or convert an unused view if
// possible.
final View child = obtainView(position, mIsScrap);
// This needs to be positioned and measured.
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}
- mRecycler.getActiveView(position); 从缓存中获取每个child,此时返回的View有可能为空
- obtainView 方法的调用是传入缓存中取出的convertView,最终调用adapter的getView方法确定每个child的值,obtainView在AbsListView中具体实现
- setupChild方法 设置child的大小和布局位置
4 AbsListView 的obtainView 方法
obtainView:中会执行以下内容
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
if (child != scrapView) {
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
} else if (child.isTemporarilyDetached()) {
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
}
}
从上面可以看出child是从adapter中获取
(我们在给ListView设置适配器时,如果getView返回为null会报错,就是因为此处的调用)
- mRecycler 是缓存,是ABSListView的内部类RecycleBin,addScrapView添加缓存,getScrapView获取缓存
- 在addScrapView中会调用 mScrapViews[viewType].add(scrap);
,mScrapView的声明:
private ArrayList[] mScrapViews;
即通过ArrayList实现缓存View
5 缓存机制
除了RecycleBin缓存View,ListView高效加载的原因,在于只绘制了当前屏幕的数据,向下滑动的时候,再去获取对应的View(从缓存获取,缓存没有调用adapter的getView方法),加载显示
//复用机制的核心,控制了一页屏的绘制
while (nextBottom > end && pos >= 0) {
// is this the selected item?
boolean selected = pos == mSelectedPosition;
View child = makeAndAddView(pos, nextBottom, false, mListPadding.left, selected);
nextBottom = child.getTop() - mDividerHeight;
if (selected) {
selectedView = child;
}
pos--;
}
代码中的child是下一个child,end是屏幕底部,当nextBottom>end时,停止while循环,整个屏幕填充完成。