Android ListView初始化簡單分析
yangxi_001發表於2017-07-31
下面是分析ListView初始化的原始碼流程分析,主要是ListVIew.onLayout過程與普通檢視的layout過程完全不同,避免流程交代不清楚,以下是一個流程的思維導圖。
思維導圖是順序是從左向右,從上向下。
一、 先看建構函式,上圖中1.1就不分析了,主要是讀取一些ListView引數,直接來看1.2 ViewGroup建構函式原始碼
- private void initViewGroup() {
- ......
- // 初始化儲存當前ViewGroup中所有View的陣列
- mChildren = new View[ARRAY_INITIAL_CAPACITY];
- // 初始時其Child個數為0
- mChildrenCount = 0;
- ......
- }
檢視的建立過程的都會執行的三個步驟: onMeasure, onLayout, onDraw
- @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的不同
- @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
- @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原始碼
- // 從上向下在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原始碼
- // 在引數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原始碼
- // 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原始碼
- 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:ListView 的簡單用法2016-01-08AndroidView
- ListView簡單使用2016-08-19View
- ListView 與 RecyclerView 簡單對比2018-08-25View
- QT - 13.1.1 ListView 的簡單使用2020-11-10QTView
- 一隻android簡訊控制馬的簡單分析2020-08-19Android
- 用ListView簡單實現滑動列表2019-01-22View
- 簡單實現具有彈性的ListView2017-12-26View
- Android ListView2014-04-04AndroidView
- React Native學習之 ListView 的簡單使用2017-12-27React NativeView
- RecyclerView超簡單demo(區域性更新取代ListView)2016-06-08View
- Android之ListView2014-01-20AndroidView
- Android的ListView2014-04-24AndroidView
- android開發中Settings結構簡單分析2016-08-03Android
- ExplosionField簡單分析2017-03-05
- android中的ListView2020-03-09AndroidView
- android listview and scrollview2014-02-10AndroidView
- android:ListView bbs Demo2016-01-08AndroidView
- 初始化ArrayList的簡單方法總結2018-06-18
- Flutter ListView 原始碼分析2019-06-17FlutterView原始碼
- Android實現錄屏直播(一)ScreenRecorder的簡單分析2018-11-23Android
- Golang專案簡單初始化快取池2021-06-16Golang快取
- 初始化簡單的IP放火牆(Script)(轉)2007-08-10
- Android ListView元件樣式2015-01-13AndroidView元件
- Android裡透明的ListView2013-06-07AndroidView
- android listview checkedTextView 的使用2013-09-02AndroidTextView
- mr原理簡單分析2020-08-23
- SSRF漏洞簡單分析2020-07-16
- 簡單陰影分析2020-12-27
- Dubbo原理簡單分析2017-04-13
- 安卓開發——ListView控制元件(初始化ListView、列表重新整理、長按新增menu)2020-04-05安卓View控制元件
- Android Scroller簡單用法2015-12-18Android
- Android AsyncTask簡單用法2012-11-12Android
- 一個簡單的filamentphp後臺初始化模板2023-11-24PHP
- android:定製 ListView 的介面2016-01-08AndroidView
- Android listview與adapter用法2014-04-21AndroidViewAPT
- Android ListView(Selector 顏色)2014-04-26AndroidView
- Android入門之ListView (二)2010-08-15AndroidView
- Android入門之ListView (一)2010-08-15AndroidView