android view.measure的用法,自定义View--View的measure过程

DecorView

窗口的顶级布局:包含了titlebar(标题栏)、content(内容android.R.id.content)

通过android.R.id.content获得内容布局的Parent层:

ViewGroup content = (ViewGroup)findViewById(android.R.id.content) ;

View view = content.getChildAt(0);

View的measure过程

MeasureSpec 代表32位的int值,高2位代表SpecMode(测量模式) ,低30位代表SpecSize(测量模式下的规格大小)

1.UNSPECIFIED

父容器不对Viewyou任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态,一般属于系统内部多次Measure的情形,一般来说不需要关注此模式

2.EXACTLY

父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的的值,他对应于LayoutParams中的match_parent 和具体的数值这两种模式

3.AT_MOST

父容器指定了一个可用大小即SpecSize,View 的大小不能大于这个值具体是什么要看不同View的具体实现。他对应于LayoutParams中的wrap_content

parentSpecMode

EXACTLY

AT_MOST

UNSPECIFIED

chlidLayoutParams

dp/px(固定数值)

EXACTLY childSize

EXACTLY childSize

EXACTLY childSize

match_parent

EXACTLY parentSize

AT_MOST parentSize

UNSPECIFIED 0

wrap_content

AT_MOST parentSize

AT_MOST parentSize

UNSPECIFIED 0

从上边的表中可以看出View的match_parent 和 wrap_content 是一样的结果都是parent剩余的大小,这样跟我们的实际结果是不一样的,实际上在View这种情况先是进行了一定的处理的

具体代码是这样的:

public class mView extends View {

public mView(Context context) {

super(context);

}

private int mWidth = 0;

private int mHeight = 0;

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(mWidth, mHeight);

} else if (widthSpecMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(mWidth, heightSpecSize);

} else if (heightSpecMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(widthSpecSize, mHeight);

}

}

}

在measure之后通过getMeasureHeight/Height ,但是尽量不要在measure之后获得,好的习惯是在onLayout中获得,但是有的时候我们在Activity的onCreat中获得measure宽高,但是View没有测完,所以为0,现在解决方案有4种:

Activity /View#onWindowFocusChanged

@Override

public void onWindowFocusChanged(boolean hasWindowFocus) {

super.onWindowFocusChanged(hasWindowFocus);

if (hasWindowFocus) {

int width = getMeasuredWidth();

int height = getMeasuredHeight();

}

}

view.post(runnable)

@Override

protected void onStart() {

super.onStart();

view.post(new Runnable() {

@Override

public void run() {

int width = view.getMeasuredWidth();

int height = view.getMeasuredHeight();

}

});

Log.e(TAG, "onStart()");

}

ViewTreeObserver

ViewTreeObserver observer = view.getViewTreeObserver();

observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)

@SuppressWarnings("deprecation")

@Override

public void onGlobalLayout() {

view.getViewTreeObserver().removeOnGlobalLayoutListener(this);

int width = view.getMeasuredWidth();

int height = view.getMeasuredHeight();

}

});

view.measure(int widthMeasureSpec, int heightMeasureSpec)

这种比较复杂,根据View的LayoutParams来分:

match_parent

还是直接放弃吧

wrap_content

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);

int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);

view.measure(widthMeasureSpec, heightMeasureSpec);

注意到(1<<30)-1 ,通过分析MeasureSpec的实现可以知道,View的尺寸使用30位二进制表示也就是说最大是30个1(即2^30 -1 ),也就是(1<<30)-1,在最大化模式下,我们用View理论上能支持的最大值去构造MeasureSpec是合理的

+ 具体数值(dp/px)

//比如宽/高 都是100px 如下measure

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);

int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);

view.measure(widthMeasureSpec,heightMeasureSpec);

错误用法:无法通过错误的MeasureSpec去得出合法的SpecMode,从而导致measure过程出错

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);

int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);

view.measure(widthMeasureSpec, heightMeasureSpec);

view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值