Android ListView初始化简单分析
2015-07-18 18:46
453 查看
下面是分析ListView初始化的源码流程分析,主要是ListVIew.onLayout过程与普通视图的layout过程完全不同,避免流程交代不清楚,以下是一个流程的思维导图。
思维导图是顺序是从左向右,从上向下。
一、 先看构造函数,上图中1.1就不分析了,主要是读取一些ListView参数,直接来看1.2 ViewGroup构造函数源码
[java] view
plaincopyprint?
private void initViewGroup() {
......
// 初始化保存当前ViewGroup中所有View的数组
mChildren = new View[ARRAY_INITIAL_CAPACITY];
// 初始时其Child个数为0
mChildrenCount = 0;
......
}
视图的创建过程的都会执行的三个步骤: onMeasure, onLayout, onDraw
二、接着2 即 ListView.onMeasure方法,只是获取当前ListView的宽高
[java] view
plaincopyprint?
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取当前ListView总宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
......
setMeasuredDimension(widthSize , heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}
三、步骤3是重点,AbsListView.onLayout的流程与普通View的不同
[java] view
plaincopyprint?
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
// 初始时changed = true
if (changed) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// ?
getChildAt(i).forceLayout();
}
mRecycler.markChildrenDirty();
}
if (mFastScroller != null && mItemCount != mOldItemCount) {
mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
}
// ListView实现此方法
layoutChildren();
mInLayout = false;
mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
}
四、步骤4.1 具体分析ListVIew.layoutChildren
[java] view
plaincopyprint?
@Override
protected void layoutChildren() {
// 默认为false
final boolean blockLayoutRequests = mBlockLayoutRequests;
if (!blockLayoutRequests) {
// 目的是为了阻止没必要的layout操作,提交效率
mBlockLayoutRequests = true;
} else {
return;
}
try {
super.layoutChildren();
// 为什么此处要请求重绘?
invalidate();
......
int childCount = getChildCount();
......
boolean dataChanged = mDataChanged;
if (dataChanged) {
handleDataChanged();
}
......
// 把所有child view放置到RecycleBin
// 满足条件的话可以重用这些child view
final int firstPosition = mFirstPosition;
// ListView中Item复用使用此数据结构
final RecycleBin recycleBin = mRecycler;
// reset the focus restoration
View focusLayoutRestoreDirectChild = null;
// Don't put header or footer views into the Recycler. Those are
// already cached in mHeaderViews;
if (dataChanged) {
for (int i = 0; i < childCount; i++) {
recycleBin.addScrapView(getChildAt(i), firstPosition+i);
}
} else {
// 创建childCount个数的View放入RecycleBin类activeViews数组中
recycleBin.fillActiveViews(childCount, firstPosition);
}
......
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
switch (mLayoutMode) {
......
default:
if (childCount == 0) {
if (!mStackFromBottom) {
final int position = lookForSelectablePosition(0, true);
setSelectedPositionInt(position);
// 此方法是重点,以下具体分析
sel = fillFromTop(childrenTop);
} else {
final int position = lookForSelectablePosition(mItemCount - 1, false);
setSelectedPositionInt(position);
sel = fillUp(mItemCount - 1, childrenBottom);
}
} else {
if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
sel = fillSpecific(mSelectedPosition,
oldSel == null ? childrenTop : oldSel.getTop());
} else if (mFirstPosition < mItemCount) {
sel = fillSpecific(mFirstPosition,
oldFirst == null ? childrenTop : oldFirst.getTop());
} else {
sel = fillSpecific(0, childrenTop);
}
}
break;
}
// Flush any cached views that did not get reused above
recycleBin.scrapActiveViews();
......
invokeOnItemScrollListener();
} finally {
if (!blockLayoutRequests) {
mBlockLayoutRequests = false;
}
}
五、 分析步骤4.2 ListView.fillFromTop源码
[java] view
plaincopyprint?
// 从上向下在ListView中填充Item View
private View fillFromTop(int nextTop) {
mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);
if (mFirstPosition < 0) {
mFirstPosition = 0;
}
// 具体操作在此
return fillDown(mFirstPosition, nextTop);
}
六、查看步骤4.3 ListView.fillDown源码
[java] view
plaincopyprint?
// 在参数pos下面填充Item View
private View fillDown(int pos, int nextTop) {
View selectedView = null;
// ListView getHeight也是这样计算的
int end = (mBottom - mTop);
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
end -= mListPadding.bottom;
}
// 初始化时pos = 0,如果item总数少于一屏幕,执行mItemCount - pos次
// 如果item多余一屏幕,执行end - nextTop次
while (nextTop < end && pos < mItemCount) {
// is this the selected item?
boolean selected = pos == mSelectedPosition;
// 获取Item View对象
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;
}
七、查看步骤4.4 ListView.makeAndAddView源码
[java] view
plaincopyprint?
// ListView都是通过此方法获取Item View
// 具体Item View如何复用,是否需要创建新的Item View都有此方法处理
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;
ListView的数据发生变化,肯定Item View之前已经创建好了,无需重新创建
if (!mDataChanged) {
// 当前position复用之前创建的视图
child = mRecycler.getActiveView(position);
if (child != null) {
// 对复用的View针对当前需要进行配置
setupChild(child, position, y, flow, childrenLeft, selected, true);
return child;
}
}
// 创建或者重用视图
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}
八 查看步骤4.5 ListView.setupChild源码
[java] view
plaincopyprint?
private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
boolean selected, boolean recycled) {
// 判断当前Item View是否选中状态
final boolean isSelected = selected && shouldShowSelector();
final boolean updateChildSelected = isSelected != child.isSelected();
final int mode = mTouchMode;
// 是否处于按下状态
final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&
mMotionPosition == position;
final boolean updateChildPressed = isPressed != child.isPressed();
// 是否需要重新measure与layout
final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();
// Respect layout params that are already in the view. Otherwise make some up...
// noinspection unchecked
AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
if (p == null) {
p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
}
p.viewType = mAdapter.getItemViewType(position);
if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&
p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
attachViewToParent(child, flowDown ? -1 : 0, p);
} else {
p.forceAdd = false;
if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
p.recycledHeaderFooter = true;
}
// 向ListView(ViewGroup子类)添加当前Item View
addViewInLayout(child, flowDown ? -1 : 0, p, true);
}
// 更新选中状态
if (updateChildSelected) {
child.setSelected(isSelected);
}
// 更新按下状态
if (updateChildPressed) {
child.setPressed(isPressed);
}
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
if (child instanceof Checkable) {
((Checkable) child).setChecked(mCheckStates.get(position));
} else if (getContext().getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.HONEYCOMB) {
child.setActivated(mCheckStates.get(position));
}
}
if (needToMeasure) {
int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
// 与普通视图的measure流程不同,ListView是在此处执行具体的当前Item View measure
child.measure(childWidthSpec, childHeightSpec);
} else {
cleanupLayoutState(child);
}
final int w = child.getMeasuredWidth();
final int h = child.getMeasuredHeight();
final int childTop = flowDown ? y : y - h;
if (needToMeasure) {
final int childRight = childrenLeft + w;
final int childBottom = childTop + h;
// 大小改变肯定位置也会发生变化,当前Item View重新进行layout
child.layout(childrenLeft, childTop, childRight, childBottom);
} else {
child.offsetLeftAndRight(childrenLeft - child.getLeft());
child.offsetTopAndBottom(childTop - child.getTop());
}
if (mCachingStarted && !child.isDrawingCacheEnabled()) {
child.setDrawingCacheEnabled(true);
}
if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
!= position) {
child.jumpDrawablesToCurrentState();
}
}
转载请注明原文地址:http://blog.csdn.net/love_world_/article/details/8547077
思维导图是顺序是从左向右,从上向下。
一、 先看构造函数,上图中1.1就不分析了,主要是读取一些ListView参数,直接来看1.2 ViewGroup构造函数源码
[java] view
plaincopyprint?
private void initViewGroup() {
......
// 初始化保存当前ViewGroup中所有View的数组
mChildren = new View[ARRAY_INITIAL_CAPACITY];
// 初始时其Child个数为0
mChildrenCount = 0;
......
}
视图的创建过程的都会执行的三个步骤: onMeasure, onLayout, onDraw
二、接着2 即 ListView.onMeasure方法,只是获取当前ListView的宽高
[java] view
plaincopyprint?
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取当前ListView总宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
......
setMeasuredDimension(widthSize , heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}
三、步骤3是重点,AbsListView.onLayout的流程与普通View的不同
[java] view
plaincopyprint?
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
// 初始时changed = true
if (changed) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// ?
getChildAt(i).forceLayout();
}
mRecycler.markChildrenDirty();
}
if (mFastScroller != null && mItemCount != mOldItemCount) {
mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
}
// ListView实现此方法
layoutChildren();
mInLayout = false;
mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
}
四、步骤4.1 具体分析ListVIew.layoutChildren
[java] view
plaincopyprint?
@Override
protected void layoutChildren() {
// 默认为false
final boolean blockLayoutRequests = mBlockLayoutRequests;
if (!blockLayoutRequests) {
// 目的是为了阻止没必要的layout操作,提交效率
mBlockLayoutRequests = true;
} else {
return;
}
try {
super.layoutChildren();
// 为什么此处要请求重绘?
invalidate();
......
int childCount = getChildCount();
......
boolean dataChanged = mDataChanged;
if (dataChanged) {
handleDataChanged();
}
......
// 把所有child view放置到RecycleBin
// 满足条件的话可以重用这些child view
final int firstPosition = mFirstPosition;
// ListView中Item复用使用此数据结构
final RecycleBin recycleBin = mRecycler;
// reset the focus restoration
View focusLayoutRestoreDirectChild = null;
// Don't put header or footer views into the Recycler. Those are
// already cached in mHeaderViews;
if (dataChanged) {
for (int i = 0; i < childCount; i++) {
recycleBin.addScrapView(getChildAt(i), firstPosition+i);
}
} else {
// 创建childCount个数的View放入RecycleBin类activeViews数组中
recycleBin.fillActiveViews(childCount, firstPosition);
}
......
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
switch (mLayoutMode) {
......
default:
if (childCount == 0) {
if (!mStackFromBottom) {
final int position = lookForSelectablePosition(0, true);
setSelectedPositionInt(position);
// 此方法是重点,以下具体分析
sel = fillFromTop(childrenTop);
} else {
final int position = lookForSelectablePosition(mItemCount - 1, false);
setSelectedPositionInt(position);
sel = fillUp(mItemCount - 1, childrenBottom);
}
} else {
if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
sel = fillSpecific(mSelectedPosition,
oldSel == null ? childrenTop : oldSel.getTop());
} else if (mFirstPosition < mItemCount) {
sel = fillSpecific(mFirstPosition,
oldFirst == null ? childrenTop : oldFirst.getTop());
} else {
sel = fillSpecific(0, childrenTop);
}
}
break;
}
// Flush any cached views that did not get reused above
recycleBin.scrapActiveViews();
......
invokeOnItemScrollListener();
} finally {
if (!blockLayoutRequests) {
mBlockLayoutRequests = false;
}
}
五、 分析步骤4.2 ListView.fillFromTop源码
[java] view
plaincopyprint?
// 从上向下在ListView中填充Item View
private View fillFromTop(int nextTop) {
mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);
if (mFirstPosition < 0) {
mFirstPosition = 0;
}
// 具体操作在此
return fillDown(mFirstPosition, nextTop);
}
六、查看步骤4.3 ListView.fillDown源码
[java] view
plaincopyprint?
// 在参数pos下面填充Item View
private View fillDown(int pos, int nextTop) {
View selectedView = null;
// ListView getHeight也是这样计算的
int end = (mBottom - mTop);
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
end -= mListPadding.bottom;
}
// 初始化时pos = 0,如果item总数少于一屏幕,执行mItemCount - pos次
// 如果item多余一屏幕,执行end - nextTop次
while (nextTop < end && pos < mItemCount) {
// is this the selected item?
boolean selected = pos == mSelectedPosition;
// 获取Item View对象
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;
}
七、查看步骤4.4 ListView.makeAndAddView源码
[java] view
plaincopyprint?
// ListView都是通过此方法获取Item View
// 具体Item View如何复用,是否需要创建新的Item View都有此方法处理
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;
ListView的数据发生变化,肯定Item View之前已经创建好了,无需重新创建
if (!mDataChanged) {
// 当前position复用之前创建的视图
child = mRecycler.getActiveView(position);
if (child != null) {
// 对复用的View针对当前需要进行配置
setupChild(child, position, y, flow, childrenLeft, selected, true);
return child;
}
}
// 创建或者重用视图
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
return child;
}
八 查看步骤4.5 ListView.setupChild源码
[java] view
plaincopyprint?
private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
boolean selected, boolean recycled) {
// 判断当前Item View是否选中状态
final boolean isSelected = selected && shouldShowSelector();
final boolean updateChildSelected = isSelected != child.isSelected();
final int mode = mTouchMode;
// 是否处于按下状态
final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&
mMotionPosition == position;
final boolean updateChildPressed = isPressed != child.isPressed();
// 是否需要重新measure与layout
final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();
// Respect layout params that are already in the view. Otherwise make some up...
// noinspection unchecked
AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
if (p == null) {
p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
}
p.viewType = mAdapter.getItemViewType(position);
if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&
p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
attachViewToParent(child, flowDown ? -1 : 0, p);
} else {
p.forceAdd = false;
if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
p.recycledHeaderFooter = true;
}
// 向ListView(ViewGroup子类)添加当前Item View
addViewInLayout(child, flowDown ? -1 : 0, p, true);
}
// 更新选中状态
if (updateChildSelected) {
child.setSelected(isSelected);
}
// 更新按下状态
if (updateChildPressed) {
child.setPressed(isPressed);
}
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
if (child instanceof Checkable) {
((Checkable) child).setChecked(mCheckStates.get(position));
} else if (getContext().getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.HONEYCOMB) {
child.setActivated(mCheckStates.get(position));
}
}
if (needToMeasure) {
int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
// 与普通视图的measure流程不同,ListView是在此处执行具体的当前Item View measure
child.measure(childWidthSpec, childHeightSpec);
} else {
cleanupLayoutState(child);
}
final int w = child.getMeasuredWidth();
final int h = child.getMeasuredHeight();
final int childTop = flowDown ? y : y - h;
if (needToMeasure) {
final int childRight = childrenLeft + w;
final int childBottom = childTop + h;
// 大小改变肯定位置也会发生变化,当前Item View重新进行layout
child.layout(childrenLeft, childTop, childRight, childBottom);
} else {
child.offsetLeftAndRight(childrenLeft - child.getLeft());
child.offsetTopAndBottom(childTop - child.getTop());
}
if (mCachingStarted && !child.isDrawingCacheEnabled()) {
child.setDrawingCacheEnabled(true);
}
if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
!= position) {
child.jumpDrawablesToCurrentState();
}
}
转载请注明原文地址:http://blog.csdn.net/love_world_/article/details/8547077
相关文章推荐
- Android View深入学习(二),View的布局(Layout)过程
- Android keyevent 整理
- Android 实现RippleEffect水波纹效果
- android 添加图片自动换行
- android(9)_数据存储和访问3_scard基本介绍
- 对android volley框架的理解(二 )
- 【Android界面实现】整合了刷新、加载更多、滑动删除功能的XListview
- android 如何添加一种新的语言
- Android新组件RecyclerView介绍,其效率更好
- Android新组件RecyclerView介绍,其效率更好
- Android学习资源(持续更新中)
- android 环境搭建中adk 位置找不到问题解决方案
- 如何解决:Android中 Error generating final archive: Debug Certificate expired on 10/09/18 16:30 的错误
- android 项目的主要结构与详细讲解
- 【Android】资源系列(一) -- 国际化(多语言)
- Android-PullToRefresh库实现上拉刷新下拉加载
- Android.mk详解
- android颜色对应的xml配置值,颜色表
- Android Support Design 中 CoordinatorLayout 与 Behaviors 初探
- Android自定义View研究(四) -- 在XML中定义View