通过FrameLayout分析onMeasure

本文通过分析FrameLayout的源码,探讨了其在Android布局中的摆放规则,特别是如何根据layout_gravity属性控制子View的位置。文章从 DecorView 的 padding 和 margin 开始,详细解释了测量、摆放和绘制流程,特别是onMeasure()方法中的规格计算和递归测量过程,以及MeasureSpec的工作原理。同时,还介绍了在没有明确设置LayoutParams时,如何默认处理子View的测量规格。

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

首先说下为什么分析FrameLayout .
……
因为他简单啊!! 哪像相对布局和线性布局 , 源码太长了…

大家也知道FrameLayout 的摆放规则 , 大部分使用是根据layout_gravity属性来控制摆放位置 , 如果你没有使用这个属性 , 默认就是放在左上角 , 并且最后添加的子View会显示在最上层 .

那么这篇文章从DecorView开始分析它的padding , margin等 , 这些值是怎么算到onMeasure()和onLayout()的 , 以及简单讲一下addView()方法和LayoutParam .

那么开始分析下

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <TextView
        android:text="123"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</FrameLayout>

上篇文章中讲到 , Activity的setContentView()方法 , 执行完setContentView()方法后 , 这个布局已经添加到了DecorView上面了. 其实setContentView()方法里面也是调用了addView()方法将子View添加到父View上去的 , 再看addView()方法 , 他调用了requestLayout()方法 , 但是那么根据上篇文章讲到的requestLayout()方法会执行ViewRootImpl的requestLayout()接着调用performTraversal()方法来执行测量 , 摆放 , 绘制 .
但是!
在setContentView()的时候虽然调用了addView()方法 , 但是这时ViewRootImpl还没有实例化 , 它的实例化其实是在activity.attach()方法中 new 出来的 , 所以第一次的performTraversal()方法是在 handleResume()调用的 .
那么再重新屡一下DecorView在添加到Window之后他的第一次测量 、摆放和绘制 .
1. ActivityThread的handleResume()
2. 在handleResume()方法中调用了ViewRootImpl的setView()
3. ViewRootImpl的setView()方法中把decorView添加到了PhoneWindow
4. 接着调用了ViewRootImpl的requestLayout()
5. 最终他会在requestLayout()中调用performTraversals()之后 , 会使每个View执行他们的测量等方法

那么从DecorView开始看一下DecorView的 lp 是什么时候设置进去的并且值是什么

    DecorView(Context context, int featureId, PhoneWindow window,
            WindowManager.LayoutParams params) {
        ......
    }
    protected DecorView generateDecor(int featureId) {
        ......
        return new DecorView(context, featureId, this, getAttributes());
    }

可以看到DecorView构造函数的最后一个参数就是DecorView的LayoutParams

    private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();

    public final WindowManager.LayoutParams getAttributes() {
        return mWindowAttributes;
    }

getAttributes()获得的其实就是mWindowAttributes

        public LayoutParams() {
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;
            format = PixelFormat.OPAQUE;
        }

答案在WindowManager的LayoutParams的无参构造方法当中 , 也就是DecorView的lp宽高都是MATCH_PARENT .

接着看performTraversals()中的performMeasure()方法

    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    private static int getRootMeasureSpec(int windowSize, 
E FATAL EXCEPTION: main Process: com.example.kucun2, PID: 20791 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:454) at android.widget.ArrayAdapter.getView(ArrayAdapter.java:416) at com.example.kucun2.function.Adapter$5.getView(Adapter.java:158) at android.widget.AbsSpinner.onMeasure(AbsSpinner.java:206) at android.widget.Spinner.onMeasure(Spinner.java:622) at androidx.appcompat.widget.AppCompatSpinner.onMeasure(AppCompatSpinner.java:444) at android.view.View.measure(View.java:29007) at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1226) at android.widget.LinearLayout.onMeasure(LinearLayout.java:728) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1613) at android.widget.LinearLayout.measureVertical(LinearLayout.java:883) at android.widget.LinearLayout.onMeasure(LinearLayout.java:726) at android.view.View.measure(View.java:29007) at android.widget.ScrollView.measureChildWithMargins(ScrollView.java:1641) at android.widget.FrameLayout.onMeasure(FrameLayout.java:205) at android.widget.ScrollView.onMeasure(ScrollView.java:559) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.FrameLayout.onMeasure(FrameLayout.java:205) at android.view.View.measure(View.java:29007) at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811) at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:466) at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:134) at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:278) at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120) at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594) at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:760) at com.google.android.material.appbar.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:100) at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:2381) at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:831) at android.view.View.measure(View.java:29007) at androidx.drawerlayout.widget.DrawerLayout.onMeasure(DrawerLayout.java:1156) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.FrameLayout.onMeasure(FrameLayout.java:205) at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1613) at android.widget.LinearLayout.measureVertical(LinearLayout.java:883) at android.widget.LinearLayout.onMeasure(LinearLayout.java:726) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.FrameLayout.onMeasure(FrameLayout.java:205) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1613) 2025-06-11 09:10:16.360 20791-20791 AndroidRuntime com.example.kucun2 E at android.widget.LinearLayout.measureVertical(LinearLayout.java:883) at android.widget.LinearLayout.onMeasure(LinearLayout.java:726) at android.view.View.measure(View.java:29007) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7132) at com.android.internal.policy.DecorView.measureChildWithMargins(DecorView.java:3010) at android.widget.FrameLayout.onMeasure(FrameLayout.java:205) at com.android.internal.policy.DecorView.onMeasure(DecorView.java:911) at android.view.View.measure(View.java:29007) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:5801) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:3999) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4393) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3667) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:12113) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:2459) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:2468) at android.view.Choreographer.doCallbacks(Choreographer.java:1693) at android.view.Choreographer.doFrame(Choreographer.java:1448) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:2284) at android.os.Handler.handleCallback(Handler.java:1014) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loopOnce(Looper.java:250) at android.os.Looper.loop(Looper.java:340) at android.app.ActivityThread.main(ActivityThread.java:9913) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:621) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
最新发布
06-12
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值