android onMeasure() 三种约束类型
先不说太多的概念,希望通过一个简单的demo来理解这三种类型。
测试Demo
OnMeasureLayout.java
package com.example.lcj.myapplication;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class OnMeasureLayout extends LinearLayout {
private static final String TAG = "OnMeasureLayout";
public OnMeasureLayout(Context context) {
super(context);
}
public OnMeasureLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OnMeasureLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
以上很简单的代码就是一个继承了LinearLayout 重写了构造方法和onMeasure方法,我们主要分析的就是onMeasure传过来的两个参数widthMeasureSpec与heightMeasureSpec。我先把代码粘完~
activity_main.xml
<com.example.lcj.myapplication.OnMeasureLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="@android:color/holo_blue_light"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="LCJLCJ"
android:textSize="150sp"
/>
</com.example.lcj.myapplication.OnMeasureLayout>
xml布局中引用OnMeasureLayout。
MainActivity.java
package com.example.lcj.myapplication;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
这个不说了,你懂的…
好运行看一下效果:
ok,没有问题,我们简单改一下OnMeasureLayout.java
中的onMeasure()方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//取出宽的MeasureSpec 中的size(大小) 和 mode(约束类型)
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//判断约束类型并打印输出
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
Log.d(TAG, "MeasureSpec.UNSPECIFIED");
break;
case MeasureSpec.AT_MOST:
Log.d(TAG,"MeasureSpec.AT_MOST");
break;
case MeasureSpec.EXACTLY:
Log.d(TAG,"MeasureSpec.EXACTLY");
break;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
因为widthMeasureSpec 与 heightMeasureSpec 分析方法是一样的,我们就只分析宽度的MeasureSpec就可以了。MeasureSpec 中有两个信息,size(大小) 和 mode(约束类型)。那么我们现在通过上面的代码取出MeasureSpec 中的两个信息进行分析。
好,我们再运行代码看一下Log:
12952-12952/com.example.lcj.myapplication D/OnMeasureLayout﹕ MeasureSpec.EXACTLY
12952-12952/com.example.lcj.myapplication D/OnMeasureLayout﹕ MeasureSpec.EXACTLY
我们看到是 EXACTLY,测量会进行多次。
好,接的我们改一下activity_main.xml
中的代码
<com.example.lcj.myapplication.OnMeasureLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="250dp"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="@android:color/holo_blue_light"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="LCJLCJ"
android:textSize="150sp"
/>
</com.example.lcj.myapplication.OnMeasureLayout>
将….OnMeasureLayout 的宽度设置为250dp ,再次运行看Log
效果如上,再看一下Log:
26504-26504/? D/OnMeasureLayout﹕ MeasureSpec.EXACTLY
26504-26504/? D/OnMeasureLayout﹕ MeasureSpec.EXACTLY
也是EXACTLY ,到这里我们发现 match_parent 和 固定值250dp 的约束类型都是 EXACTLY ,exactly字面意思就是准确地、恰好地 、等,也就是说对子view来说,给定的大小已经限定了,就这么大。这就是EXACTLY的约束能力,有点强硬。(match_parent 默认值最大就是手机屏幕了,不多说)
好接下来想必你也知道了,我们改成wrap_content 看看Log:
<com.example.lcj.myapplication.OnMeasureLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity"
android:background="@android:color/holo_blue_light"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="LCJLCJ"
android:textSize="150sp"
/>
</com.example.lcj.myapplication.OnMeasureLayout>
运行效果:
18270-18270/? D/OnMeasureLayout﹕ MeasureSpec.AT_MOST
ok,wrap_content 的约束类型是 AT_MOST,wrap_content 是包裹内容,大家都比较熟悉了,那么at_most跟包裹内容有毛关系呢?先来看看at_most 字面意思:最多、至多、等。
18270-18270/? D/OnMeasureLayout﹕ MeasureSpec.AT_MOST
那么,最多到哪啊?其实wrap_content 的最多默认到父布局的宽高,那么match_parent 不也是默认到父布局的宽高么?区别在哪里,其实区别从运行效果上来看最简单了,对于wrap_content 约束能力来说就是
子view用多少就给多少,别超过最大值就行。而对于match_parent 不管你子view 的使用情况,就给你这么大!
那么,还剩下UNSPECIFIED,我们继续改一下OnMeasureLayout.java
中的onMeasure()方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//取出宽的MeasureSpec 中的size(大小) 和 mode(约束类型)
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//判断约束类型并打印输出
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
Log.d(TAG, "MeasureSpec.UNSPECIFIED");
break;
case MeasureSpec.AT_MOST:
Log.d(TAG,"MeasureSpec.AT_MOST");
break;
case MeasureSpec.EXACTLY:
Log.d(TAG,"MeasureSpec.EXACTLY");
break;
}
//修改宽度的约束类型 specSize 不变,修改约束类型为 UNSPECIFIED
//MeasureSpec.makeMeasureSpec()用于组装一个 MeasureSpec 简单带过。
int myWidthMeasureSpec = MeasureSpec.makeMeasureSpec(specSize,MeasureSpec.UNSPECIFIED);
super.onMeasure(myWidthMeasureSpec, heightMeasureSpec);
}
这里我们先看运行效果:
这里我们看到的效果就是textview不会换行了,再看看UNSPECIFIED 字面意思 : 不指定。
这也就意味着,对子view 没有任何约束,所以也就导致上图运行的效果,因为宽度没有了约束,那就疯狂的往外长了,UNSPECIFIED 什么控件有用到?ScrollVIew,高度上就是UNSPECIFIED ,因为能滚,任性~
总结
EXACTLY
明确约束对子view大小由specSize的值来决定。AT_MOST
子view最多只能是specSize中指定的大小。UNSPECIFIED
对子view没有约束。
以上就是对三种测量约束类型的解析,大家可以把代码跑起来玩玩,这样会更好的理解。
接下来去分析相应的源码,加深理解层次。今天就到这!