自定义View(一)——measure

View的measure

对于View的绘制过程,是由ViewRootImpl.performTraversals()执行的,在performTraversals方法里,分别执行了performMeasure、performLayout、performDraw三个方法,分别是View的测量,View的位置,View的绘制,如下:

private void performTraversals(){
    ...
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    ...
    performLayout(lp, mWidth, mHeight);
    ...
    performDraw();
    ...
}

而performMeasure方法里执行了调用view的measure方法,如下:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            //看这里
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

而View的measure方法会调用onMeasure方法,如下:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    // measure ourselves, this should set the measured dimension flag back
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
}

一般我们自定义view的时候都会重写onMeasure方法,而在Activity的decorView默认是FrameLayout布局,所以,重写的是FrameLayout的onMeasure方法。

这里要讲述的就是onMeasure方法

说到View的onMeasure方法,就要说一下MeasureSpec这个类,这个类我对它的理解就是View测量的规格类,指定了几种模式:
- EXACTLY,
- AT_MOST,
- UNSPECIFIED。

  • EXACTLY:准确的、精确的;这种模式,是最容易理解和处理的,可以理解为大小固定,比如在定义layout_width的时候,定义为固定大小 10dp,20dp,或者match_parent(此时父控件是固定的)这时候,获取出来的mode就是EXACTLY
  • AT_MOST:最大的;这种模式稍微难处理些,不过也好理解,就是View的大小最大不能超过父控件,超过了,取父控件的大小,没有,则取自身大小,这种情况一般都是在layout_width设为warp_content时
  • UNSPECIFIED:不指定大小,这种情况,我们几乎用不上,它是什么意思呢,就是View的大小想要多大,就给多大,不受父View的限制,几个例子就好理解了,ScrollView控件就是

我们来看看对于View的该方法我是怎么重写的

/**
     * 整个view内容的宽高
     */
    private int contentWidth;
    private int contentHeight;

    private void initView(AttributeSet attrs) {

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //先利用MeasureSpec得到宽高的mode
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //利用MeasureSpec得到宽高的size
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //最后要设置的宽高
        int width;
        int height;

        //我们期待的宽高
        int desiredWidth = getPaddingLeft() + getPaddingRight() + contentWidth;
        int desiredHeight = getPaddingTop() + getPaddingBottom() + contentHeight;

        //根据模式来对我们需要设置的宽高进行判断
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                width = Math.min(widthSize, desiredWidth);
                break;
            case MeasureSpec.UNSPECIFIED:
                width = desiredWidth;
                break;
            default:
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
        }
        switch (heightMode) {
            case MeasureSpec.AT_MOST:
                height = Math.min(heightSize, desiredHeight);
                break;
            case MeasureSpec.UNSPECIFIED:
                height = desiredHeight;
                break;
            default:
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
        }
        //调用父类的测量方法
        setMeasuredDimension(width, height);
    }
总结:

对于自定义View,只要对onMeasure和onDraw这两个方法比较熟悉的话,基本上都可以完成一些复杂View的定义,而onMeasure方法的话,只要对MeasureSpec类比较熟悉的,基本上也不会出现问题了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值