performLayout

本文详细剖析了Android中视图的布局流程,包括初始化后的DecorView如何调用performTraversals(),以及performLayout如何通过view的layout方法来确定子view的位置。文章深入探讨了TextView和不同ViewGroup如FrameLayout及LinearLayout在onLayout中的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 初始化app后DecorView调用performTraversals(),执行完performMeasure后开始执行
    performLayout(lp, mWidthmHeight);
  2. performLayout里面调用了 view的layout方法
    host.layout(00, host.getMeasuredWidth(), host.getMeasuredHeight());
  3. 先看viewGroup的处理,里面包含两个函数:
    1. layout调用了view.layout
      super.layout(l, t, r, b)
    2. onLayout
      abstract方法,需要子类实现
  4. 所有的方法最终都是调用了view的layout,接下来看view的layout方法
    onLayout(changed, l, t, r, b)
    调用用了onLayout,而onLayout为空方法。
  5. 到这里layout执行完了,没有实现功能,说明layout的计算是在具体的view里面实现的
  6. 接下来分别看一下TextView和FrameLayout的onLayout方法
    1. TextView
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          super.onLayout(changed, left, top, right, bottom);
          if (mDeferScroll >= 0) {
              int curs = mDeferScroll;
              mDeferScroll = -1;
              bringPointIntoView(Math.min(curs, mText.length()));
          }
      }


      很简单,直接交给了super即view进行处理,原因很简单:对于单个view(没有子view)来说,只要按照父view传给的ltrb(left、top、right、bottom layout的四个参数)进行处理就好了,并不需要自己本身去计算
    2. FrameLayout
      1. void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
            final int count = getChildCount();
        
            final int parentLeft = getPaddingLeftWithForeground();
            final int parentRight = right - left - getPaddingRightWithForeground();
        
            final int parentTop = getPaddingTopWithForeground();
            final int parentBottom = bottom - top - getPaddingBottomWithForeground();
        
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        
                    final int width = child.getMeasuredWidth();
                    final int height = child.getMeasuredHeight();
        
                    int childLeft;
                    int childTop;
        
                    int gravity = lp.gravity;
                    if (gravity == -1) {
                        gravity = DEFAULT_CHILD_GRAVITY;
                    }
        
                    final int layoutDirection = getLayoutDirection();
                    final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                    final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
        
                    switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                        case Gravity.CENTER_HORIZONTAL:
                            childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                            lp.leftMargin - lp.rightMargin;
                            break;
                        case Gravity.RIGHT:
                            if (!forceLeftGravity) {
                                childLeft = parentRight - width - lp.rightMargin;
                                break;
                            }
                        case Gravity.LEFT:
                        default:
                            childLeft = parentLeft + lp.leftMargin;
                    }
        
                    switch (verticalGravity) {
                        case Gravity.TOP:
                            childTop = parentTop + lp.topMargin;
                            break;
                        case Gravity.CENTER_VERTICAL:
                            childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                            lp.topMargin - lp.bottomMargin;
                            break;
                        case Gravity.BOTTOM:
                            childTop = parentBottom - height - lp.bottomMargin;
                            break;
                        default:
                            childTop = parentTop + lp.topMargin;
                    }
        
                    child.layout(childLeft, childTop, childLeft + width, childTop + height);
                }
            }
        }


      2. 逻辑很清楚,遍历child,获得child的measureWidth和measureHeight和layoutparam,根据gravity计算childLeft和childTop,另外两个参数直接通过width和height计算获得。
      3. 最后调用child.layout进行处理
      4. 不同的viewgrouop由于自身不同的规则会在onLayout进行处理,这里的fragmeLayout比较简单,没有考虑同一个viewGroup之下的子view之间的互相影响,下面看一下LinearLayout
    3. LinearLayout
      1. @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            if (mOrientation == VERTICAL) {
                layoutVertical(l, t, r, b);
            } else {
                layoutHorizontal(l, t, r, b);
            }
        }
      2. 首先根据orientation进行区分,没毛病
      3. 看一下 layoutHorizontal
      4. void layoutHorizontal(int left, int top, int right, int bottom) {
            final boolean isLayoutRtl = isLayoutRtl();
            final int paddingTop = mPaddingTop;
        
            int childTop;
            int childLeft;
           
            // Where bottom of child should go
            final int height = bottom - top;
            int childBottom = height - mPaddingBottom; 
           
            // Space available for child
            int childSpace = height - paddingTop - mPaddingBottom;
        
            final int count = getVirtualChildCount();
        
            final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
            final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
        
            final boolean baselineAligned = mBaselineAligned;
        
            final int[] maxAscent = mMaxAscent;
            final int[] maxDescent = mMaxDescent;
        
            final int layoutDirection = getLayoutDirection();
            switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
                case Gravity.RIGHT:
                    // mTotalLength contains the padding already
                    childLeft = mPaddingLeft + right - left - mTotalLength;
                    break;
        
                case Gravity.CENTER_HORIZONTAL:
                    // mTotalLength contains the padding already
                    childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
                    break;
        
                case Gravity.LEFT:
                default:
                    childLeft = mPaddingLeft;
                    break;
            }
        
            int start = 0;
            int dir = 1;
            //In case of RTL, start drawing from the last child.
            if (isLayoutRtl) {
                start = count - 1;
                dir = -1;
            }
        
            for (int i = 0; i < count; i++) {
                final int childIndex = start + dir * i;
                final View child = getVirtualChildAt(childIndex);
                if (child == null) {
                    childLeft += measureNullChild(childIndex);
                } else if (child.getVisibility() != GONE) {
                    final int childWidth = child.getMeasuredWidth();
                    final int childHeight = child.getMeasuredHeight();
                    int childBaseline = -1;
        
                    final LinearLayout.LayoutParams lp =
                            (LinearLayout.LayoutParams) child.getLayoutParams();
        
                    if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
                        childBaseline = child.getBaseline();
                    }
                   
                    int gravity = lp.gravity;
                    if (gravity < 0) {
                        gravity = minorGravity;
                    }
                   
                    switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
                        case Gravity.TOP:
                            childTop = paddingTop + lp.topMargin;
                            if (childBaseline != -1) {
                                childTop += maxAscent[INDEX_TOP] - childBaseline;
                            }
                            break;
        
                        case Gravity.CENTER_VERTICAL:
                            // Removed support for baseline alignment when layout_gravity or
                            // gravity == center_vertical. See bug #1038483.
                            // Keep the code around if we need to re-enable this feature
                            // if (childBaseline != -1) {
                            //     // Align baselines vertically only if the child is smaller than us
                            //     if (childSpace - childHeight > 0) {
                            //         childTop = paddingTop + (childSpace / 2) - childBaseline;
                            //     } else {
                            //         childTop = paddingTop + (childSpace - childHeight) / 2;
                            //     }
                            // } else {
                            childTop = paddingTop + ((childSpace - childHeight) / 2)
                                    + lp.topMargin - lp.bottomMargin;
                            break;
        
                        case Gravity.BOTTOM:
                            childTop = childBottom - childHeight - lp.bottomMargin;
                            if (childBaseline != -1) {
                                int descent = child.getMeasuredHeight() - childBaseline;
                                childTop -= (maxDescent[INDEX_BOTTOM] - descent);
                            }
                            break;
                        default:
                            childTop = paddingTop;
                            break;
                    }
        
                    if (hasDividerBeforeChildAt(childIndex)) {
                        childLeft += mDividerWidth;
                    }
        
                    childLeft += lp.leftMargin;
                    setChildFrame(child, childLeft + getLocationOffset(child), childTop,
                            childWidth, childHeight);
                    childLeft += childWidth + lp.rightMargin +
                            getNextLocationOffset(child);
        
                    i += getChildrenSkipCount(child, childIndex);
                }
            }
        }


      5. 代码比较长,逻辑挺简单,拆开看
        1. 获取count(子view个数),childLeft(开始显示子view的left位置)
        2. 遍历child
          1. 和上面一样获取child的measureHeight和measureWidth,说明layout是在measure的基础上的
          2. 累加childLeft,水平的linearlayout子view从左到右依次显示
          3. 最终调用child.layout
  7. 综上layout过程分析完毕,总结一下
    1. 相对于measure,layout过程相对简单,view不需要特殊处理,viewgroup要特殊处理
    2. viewGroup中必须实现onLayout方法,里面根据父view的layoutGravity计算子view的显示区域和开始的显示位置
    3. 遍历view,根据子view的layoutHeigth和layoutWidth以及gravity计算子view位置
    4. 最后说一下getWidth、getHeigth和getMeasureHeight、getMeasureWidth方法
      1. public final int getWidth() {
            return mRight mLeft;
        }
      2. public final int getHeight() {
            return mBottom mTop;
        }
      3. public final int getMeasuredWidth() {
            return mMeasuredWidth MEASURED_SIZE_MASK;
        }
      4. public final int getMeasuredHeight() {
            return mMeasuredHeight MEASURED_SIZE_MASK;
        }
      5. 由上可知getWidth、getHeigth是layout后计算出来的实际值,而getMeasureHeight、getMeasureWidth则是measure计算后获取的理论值
这段代码有误,帮我改正:namespace WordFormatModifierAddIn { partial class Ribbon1 : Microsoft.Office.Tools.Ribbon.RibbonBase { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; public Ribbon1() : base(Globals.Factory.GetRibbonFactory()) { InitializeComponent(); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.tab1 = this.Factory.CreateRibbonTab(); this.group1 = this.Factory.CreateRibbonGroup(); this.buttonModifySingleDoc = this.Factory.CreateRibbonButton(); this.buttonBatchModifyDocs = this.Factory.CreateRibbonButton(); this.tab1.SuspendLayout(); this.group1.SuspendLayout(); this.SuspendLayout(); // // tab1 // this.tab1.ControlId.ControlIdType = Microsoft.Office.Tools.Ribbon.RibbonControlIdType.Office; this.tab1.Groups.Add(this.group1); this.tab1.Label = "文档格式化"; this.tab1.Name = "tab1"; // // group1 // this.group1.Items.Add(this.buttonModifySingleDoc); this.group1.Items.Add(this.buttonBatchModifyDocs); this.group1.Label = "操作"; this.group1.Name = "group1"; // // buttonModifySingleDoc // this.buttonModifySingleDoc.Label = "修改单个文档"; this.buttonModifySingleDoc.Name = "buttonModifySingleDoc"; this.buttonModifySingleDoc.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.ButtonModifySingleDoc_Click); // // buttonBatchModifyDocs // this.buttonBatchModifyDocs.Label = "批量修改文档"; this.buttonBatchModifyDocs.Name = "buttonBatchModifyDocs"; this.buttonBatchModifyDocs.Click += new Microsoft.Office.Tools.Ribbon.RibbonControlEventHandler(this.ButtonBatchModifyDocs_Click); // // Ribbon1 // this.Name = "Ribbon1"; this.RibbonType = "Microsoft.Word.Document"; this.Tabs.Add(this.tab1); this.Load += new Microsoft.Office.Tools.Ribbon.RibbonUIEventHandler(this.Ribbon1_Load); this.tab1.ResumeLayout(false); this.tab1.PerformLayout(); this.group1.ResumeLayout(false); this.group1.PerformLayout(); this.ResumeLayout(false); } #endregion internal Microsoft.Office.Tools.Ribbon.RibbonTab tab1; internal Microsoft.Office.Tools.Ribbon.RibbonGroup group1; internal Microsoft.Office.Tools.Ribbon.RibbonButton buttonModifySingleDoc; internal Microsoft.Office.Tools.Ribbon.RibbonButton buttonBatchModifyDocs; } partial class ThisRibbonCollection { internal Ribbon1 Ribbon1 { get { return this.GetRibbon<Ribbon1>(); } } } }
最新发布
03-16
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.hjschoolhelper210301201_1, PID: 13135 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference at com.example.hjschoolhelper210301201_1.ClubinfoAdpter.getView(ClubinfoAdpter.java:44) at android.widget.AbsListView.obtainView(AbsListView.java:2458) at android.widget.ListView.makeAndAddView(ListView.java:2067) at android.widget.ListView.fillDown(ListView.java:793) at android.widget.ListView.fillFromTop(ListView.java:855) at android.widget.ListView.layoutChildren(ListView.java:1838) at android.widget.AbsListView.onLayout(AbsListView.java:2255) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1855) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:536) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729) at android.widget.LinearLayout.onLayout(LinearLayout.java:1638) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) at android.widget.FrameLayout.onLayout(FrameLayout.java:270) at com.android.internal.policy.DecorView.onLayout(DecorView.java:799) at android.view.View.layout(View.java:23693) at android.view.ViewGroup.layout(ViewGroup.java:6413) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3999) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3372) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2328) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9087) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239) at android.view.Choreographer.doCallbacks(Choreographer.java:899) at android.view.Choreographer.doFrame(Choreographer.java:832) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
05-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值