-
主要思路
ViewGroup的遍歷子節(jié)點(diǎn),通過setFrame存儲位置信息 -
主體函數(shù)
View.layout(),View.onLayout(),View.setFrame() -
layout(int l, int t, int r, int b)- 作用:為自身及其子
View分配大小與位置 - 如何開始:
ViewRootImpl在performTravesals中調(diào)用DecorView.layout() - 相關(guān)源碼:
android/view/ViewRootImpl.java private void performTraversals() { performLayout(lp, mWidth, mHeight); } private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { final View host = mView; //執(zhí)行DecorView的layout方法,傳入的是measuredWidth,measuredHeight 因此得先measure,后layout host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); }android/view/View.java public void layout(int l, int t, int r, int b) { //判斷是否需要重新測量 if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } //存儲之前的l t r b信息 int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; //確定布局是否發(fā)生了變化,并存儲新的l t r b boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { //執(zhí)行布局相應(yīng)的onLayout() onLayout(changed, l, t, r, b); ListenerInfo li = mListenerInfo; if (li != null && li.mOnLayoutChangeListeners != null) { ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { //當(dāng)布局發(fā)生變化時,通知觀察者布局變化通知 listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } }
- 作用:為自身及其子
-
setFrame(int left, int top, int right, int bottom)- 作用:存儲傳入的
left,top,right,bottom信息 并確定布局是否發(fā)生變化,如果發(fā)生變化,返回true - 相關(guān)源碼:
android/view/View.java protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; //如果傳入的位置信息跟之前的位置信息不同 if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { //則說明布局發(fā)生改變 changed = true; //重新存儲位置信息 //這里注意一點(diǎn),mLeft,mTop,mRight,mBottom都是相對位置,不是坐標(biāo)系的絕對位置 //是相對于父view的位置大小 mLeft = left; mTop = top; mRight = right; mBottom = bottom; } //返回布局是否發(fā)生改變 return changed; }
- 作用:存儲傳入的
-
onLayout(boolean changed, int left, int top, int right, int bottom)- 作用:確定子
View的位置與大小,所以,如果是View,onLayout是空實(shí)現(xiàn),而如果是ViewGroup,onLayout方法是抽象,必須有具體的子類根據(jù)不同策略來實(shí)現(xiàn)。 - 相關(guān)源碼:
android/view/View.java //空實(shí)現(xiàn) protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }android/view/ViewGroup.java //抽象方法 protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
- 作用:確定子
-
getWidth()/getHeight()- 作用:
setFrame()存儲布局傳入的信息,View的寬高,也是最終的寬高。 - 相關(guān)源碼:
public final int getWidth() { //根據(jù)布局傳入的right - left得到寬 return mRight - mLeft; } public final int getHeight() { //根據(jù)布局傳入的bottom - top得到高 return mBottom - mTop; }
- 作用:
-
例子
- 以
LinearLayout來舉例onLayout在ViewGroup中是如何實(shí)現(xiàn)的 - 相關(guān)源碼:
android/widget/LinearLayout.java protected void onLayout(boolean changed, int l, int t, int r, int b) { //根據(jù)LinearLayout的方向來決定子view的布局 if (mOrientation == VERTICAL) { layoutVertical(l, t, r, b); } else { layoutHorizontal(l, t, r, b); } } void layoutVertical(int left, int top, int right, int bottom) { //獲取子view個數(shù) final int count = getVirtualChildCount(); //遍歷子view for (int i = 0; i < count; i++) { //這里我們不關(guān)注子view的left top right bottom是如何計(jì)算的 主要關(guān)注整體布局流程 setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); } } private void setChildFrame(View child, int left, int top, int width, int height) { //執(zhí)行child.layout,繼續(xù)完成子類的布局設(shè)置 child.layout(left, top, left + width, top + height); }
- 以
-
簡要流程
-
其他要點(diǎn)
-
getWidth/getHeight是在setFrame()中得到的,也就是在layout流程后才能獲取到,也是View的最終寬高。 -
layout流程中,用到的left,top,right,bottom都是相對于父View的位置,不是坐標(biāo)系的絕對位置。
-
- 系列文章
自定義View——Layout
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
