Android自定义控件onMeasure、onLayout介绍

概要:

Android自定义控件onMeasure、onLayout介绍

| |目录

记录一下自定义ViewGroup的onMeasure、onLayout;

这两个方法的执行顺序是,先执行onMeasure计算大小,然后执行onLayout来布局。

onMeasure:

先说一下MeasureSpec.AT_MOST、MeasureSpec.EXACTLY、MeasureSpec.UNSPECIFIED;

    MeasureSpec.makeMeasureSpec(size, mode)

MeasureSpec.EXACTLY:父视图希望子视图的大小应该是size中指定的。

MeasureSpec.AT_MOST:子视图的大小最多是size中指定的值,也就是说不建议子视图的大小超过size中给定的值。

MeasureSpec.UNSPECIFIED:我们可以随意指定视图的大小。


在onMeasure中计算子View的宽、高,

1.子View的宽、高为包裹内容:

for (int i = 1; i < getChildCount(); i++) {
    View childView = getChildAt(i);
    // 设定子view宽、高
    childView.measure(
        MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST),
	MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST));
}

2.指定子View的宽、高:

for (int i = 1; i < getChildCount(); i++) {
    View childView = getChildAt(i);
    // 设定子view宽、高
    childView.measure(
        MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
	MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
}

其中100可以自己随意修改,单位是px(像素),如果想使用dp单位的话

//转换dip为px 
public static int convertDIP2PX(Context context, int dip) { 
    float scale = context.getResources().getDisplayMetrics().density; 
    return (int)(dip*scale + 0.5f*(dip>=0?1:-1)); 
}

当然也可以去晚上找。

3.不设定子View的宽、高,

for (int i = 1; i < getChildCount(); i++) {
    View childView = getChildAt(i);
    // 设定子view宽、高
    childView.measure(
        MeasureSpec.UNSPECIFIED,
	MeasureSpec.UNSPECIFIED);
}

也可以这样写

for (int i = 1; i < getChildCount(); i++) {
    View childView = getChildAt(i);
    // 设定子view宽、高
    childView.measure(
        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
	MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
}

当然,如果想要让这个自定义ViewGroup的宽高包裹内容的话,只需要获取子view的宽、高来计算,然后调用setMeasuredDimension(int measuredWidth, int measuredHeight)来设置一下就OK了。

//我这里每行只放一个View,
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int parentWidthSize = 0;
    int paretnHeightSize = 0;
    for (int i = 1; i < getChildCount(); i++) {
        View childView = getChildAt(i);
        // 设定子view宽、高
        childView.measure(
            MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY),
    	    MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY));
    	parentWidthSize = childView.getMeasuredWidth();
    	paretnHeightSize += childView.getMeasuredHeight();
    }
    setMeasuredDimension(parentWidthSize, paretnHeightSize);
}


onLayout

直接上代码

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 1; i < getChildCount(); i++) {
        View childView = getChildAt(i);
        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();
        			
        childView.layout(l, t, r, b);
    }
}

一步一步看

childView.layout(l, t, r, b);

其中的

l是childView在这个自定义Viewgroup中距离左边的距离,

t是距离上边的距离,

r是自定义Viewgroup中多宽,

b是自定义Viewgroup中多高。

可以这样理解,把这个子View看作一个矩形,那么就可以把l、t看作是矩形左上角的坐标,r、b看作是右下角的坐标,这样比较好理解

上面已经在onMeasure中计算过子View的宽高了,这里可以直接用,也就是这样写

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    for (int i = 1; i < getChildCount(); i++) {
        View childView = getChildAt(i);
        //这里获取上面计算过的 子View的宽、高
        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();
        			
        childView.layout(0, 0, childWidth , childHeight );
    }
}


评论关闭
评论 还能输入200
评论关闭
评论 还能输入200
资料加载中...
已关注 , 取消