View.onMeasure()

本文详细解析了Android中View的大小是如何通过measure过程确定的,包括match_parent和wrap_content的不同行为,以及spec如何从父视图传递到子视图,并最终决定View的宽度。

问题:Android是如何确定View大小的?


width和height是独立的,可以分开考虑,所以我们只关注width;

假设:

1)我们先自定义三个View A,B,C,全部继承自LinearLayout;

2)我们的测试机的屏幕宽度是1080;


实验1, 如果全部跟着父控件走:


<A with=“match_parent">

   <B  with="match_parent'>

         <C witdh="match_parent"

        </C>

   </B>

</A>


则执行顺序是:

A EXACTLY 1080 (A的上级是1080宽度,而A的愿望是match_parent, 所以上级给A的spec就是EXACTLY 1080)

B EXACTLY 1080 (A的spec是EXACTLY 1080, B自己的愿望是match_parent,所以A给B的spec就是EXACTLY 1080)

C EXACTLY 1080 (B的spec是EXACTLY 1080, 而C自己的愿望是match_parent, 所以B给C的spec就是EXACTLY 1080)

C.width = 1080 (C的spec是EXACTLY 1080,  于是宽度就确定为1080)

B.width = 1080 (C的spec是EXACTLY 1080,  于是宽度就确定为1080)

A.width = 1080(C的spec是EXACTLY 1080,  于是宽度就确定为1080)

spec从上往下传递,而最终size的确定顺序是从下往上的;


实验2,如果全部跟着子控件走:

<A with="wrap_content">

   <B  with="wrap_content'>

        <C witdh="wrap_content"

        </C>

   </B>

</A>


则执行顺序是:

A AT_MOST 1080(A的上级是1080的,而A想包含内容,所以上级就规定A最多1080宽)

B AT_MOST 1080 (A最多1080,而B想包含内容,所以A规定B最多1080宽)

C AT_MOST 1080 (B最多1080,而C想包含内容,所以B规定C最多1080宽)

C.width = 0 (C的spec是AT_MOST 1080, 而C作为LinearLayout的子类,由于没有内容,所以决定取0,其实这儿C只要不超过1080,可以随便取值的)

B.width = 0 (B的spec是AT_MOST 1080, 而C已经确定为0,为了wrap C,B可以取0到1080之间的任意值,这儿实际取值0,注意B既考虑了第一遍从上而下传过来的spec,也考虑了child的宽度)

A.width = 0 (A的spec是AT_MOST 1080, 而B已经确定为0,为了wrap B,A可以取0到1080之间的任意值,这儿实际取值0)


实验3,parent让child定,child让parent定,简单点,2级就能说明问题:

<A with="wrap_content">

   <B  with="match_parent'>

   </B>

</A>


则执行顺序是:

A AT_MOST 1080(A的上级是1080的,而A想包含内容,所以上级就规定A最多1080宽)

B AT_MOST 1080 (A最多1080,而B想跟parent也一样大,由于A此时只知道不超过1080,并没有确定具体的值,所以A也只能规定B最多1080)

B.width = 0 (B的spec是AT_MOST 1080,  所以可以在0到1080任意取值,实际取值0)

A.width = 0 (A的spec是AT_MOST 1080, 而B已经确定为0,为了wrap B,A可以取0到1080之间的任意值,这儿实际取值0)



规律:

1)spec从上往下传递,而最终size的确定顺序是从下往上的;

2)第一遍是从上往下走,每个parent根据自己的spec,结合child的parameter,计算出给child的spec;

第二遍从下往上确定大小,每个child应该尊重parent给的spec,当然了,如果你非要违反,也没问题,等轮到parent决定大小的时候,可以根据child的实际大小见机行事,比如当child超过自己的时候引入滚动;

每个parent在决定大小的时候,既要考虑第一轮传下来的spec,也要考虑child已经确定好的大小,至于如何根据child的大小行事,就看你怎么实现onMeasure()方法 了,

通常默认的onMeasure()实现不是你想要的效果;

3)wrap_content应该了解为大于等于,而不是等于,只要能包含child就可以;

4)UNSPECIFIED在我们的例子里都没出现,这种spec估计是用在ScollView或者ListView里(height上),parent完全不给child任何的约束,child想多大就多大;






01-06 08:13:45.480248 19626 19626 E AndroidRuntime: FATAL EXCEPTION: main Process: com.aiyu.kaipanla, PID: 19626 java.lang.NumberFormatException: For input string: "null" at jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054) at jdk.internal.math.FloatingDecimal.parseFloat(FloatingDecimal.java:122) at java.lang.Float.parseFloat(Float.java:558) at java.lang.Float.valueOf(Float.java:522) at com.aiyu.kaipanla.push.PushMessageDialog$a.getView(SourceFile:8) at android.widget.AbsListView.obtainView(AbsListView.java:2492) at android.widget.GridView.onMeasure(GridView.java:1086) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608) at android.widget.LinearLayout.measureVertical(LinearLayout.java:878) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608) at android.widget.LinearLayout.measureVertical(LinearLayout.java:878) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608) at android.widget.LinearLayout.measureVertical(LinearLayout.java:878) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1608) at android.widget.LinearLayout.measureVertical(LinearLayout.java:878) at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) at android.view.View.measure(View.java:29099) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7198) at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) at com.android.internal.policy.DecorView.onMeasure(DecorView.java:835) at android.view.View.measure(View.java:29099) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:5753) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:3902) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4325) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3593) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:12031) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1852) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1861) at android.view.Choreographer.doCallbacks(Choreographer.java:1373) at android.view.Choreographer.doFrame(Choreographer.java:1230) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1835) at android.os.Handler.handleCallback(Handler.java:1027) at android.os.Handler.dispatchMessage(Handler.java:108) at android.os.Looper.loopOnce(Looper.java:298) at android.os.Looper.loop(Looper.java:408) at android.app.ActivityThread.main(ActivityThread.java:9993) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:613) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1074)
01-07
FATAL EXCEPTION: main Process: com.example.kucun2, PID: 14960 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.ui.jinhuo.AddInventoryFragment$7.getView(AddInventoryFragment.java:224) 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-07 20:25:16.069 14960-14960 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-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值