自定义view的测量onMeasure, 3种模式的处理

为什么要知道测试方法,测试模式呢?

因为自定义view在一下模式下面,需要自己对应的宽度和高度,或者需要自由的定义。

View类中的方法onMeasure() 是用来测量当前view的宽度和高度的, 3种模式说明

测量方法 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
测量模式int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

MeasureSpec.UNSPECIFIED 不确定值
MeasureSpec.AT_MOST 最大值
MeasureSpec.EXACTLY 完全准确值

以一个列子来说明?
自定义view 通过测量控制view的宽度和高度,并draw图片的一个view

public class ThumbView extends View {

    private static final String TAG = "ThumbView";
    private Bitmap thumbUp;
    private Paint mBitmapPaint;
    private int startX;
    private int startY;

    public ThumbView(Context context) {
        this(context, null);
    }

    public ThumbView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ThumbView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        thumbUp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_messages_like_selected);
        mBitmapPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);//如果是继承的viewgroup比如linearlayout时,可以先计算
        int widthResult = 0;
        //view根据xml中layout_width和layout_height测量出对应的宽度和高度值,
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        switch (widthSpecMode){
            case MeasureSpec.UNSPECIFIED:
                widthResult = widthSpecSize;
                break;
            case MeasureSpec.AT_MOST://wrap_content时候
                widthResult = getContentWidth();
                break;
            case MeasureSpec.EXACTLY:
                //当xml布局中是准确的值,比如200dp是,判断一下当前view的宽度和准确值,取两个中大的,这样的好处是当view的宽度本事超过准确值不会出界
                //其实可以直接使用准确值
                widthResult = Math.max(getContentWidth(), widthSpecSize);
                break;
        }

        int heightResult = 0;
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (heightSpecMode){
            case MeasureSpec.UNSPECIFIED:
                heightResult = heightSpecSize;
                break;
            case MeasureSpec.AT_MOST://wrap_content时候
                heightResult = getContentHeight();
                break;
            case MeasureSpec.EXACTLY:
                heightResult = Math.max(getContentHeight(), heightSpecSize);
                break;
        }
        setMeasuredDimension(widthResult, heightResult);
    }

    /**
     * view中大拇指图片和padding的宽度, 就是自己本身包括的内容总的宽度
     */
    private int getContentWidth(){
        float contentWidth = thumbUp.getWidth()+getPaddingLeft()+getPaddingRight();
        Log.d(TAG, "getContentWidth: contentWidth="+contentWidth);
        return (int)contentWidth;
    }

    int getContentHeight(){
        float contentHeight = thumbUp.getHeight()+getPaddingTop()+getPaddingBottom();
        Log.d(TAG, "getContentWidth: contentHeight="+contentHeight);
        return (int)contentHeight;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
//        startX = w/2 - thumbUp.getWidth()/2 - getPaddingLeft()/2 -  getPaddingRight()/2;
//        startY = h/2 - thumbUp.getHeight()/2 - getPaddingTop()/2 -  getPaddingBottom()/2;
        Log.d(TAG, "onSizeChanged: w="+w+",h="+h+",startX="+startX+",startY="+startY);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制图片
        canvas.drawBitmap(thumbUp, startX+getPaddingLeft(), startY+getPaddingTop(), mBitmapPaint);
    }
view实现会在XML布局文件中使用,有3中情况

1: android:layout_width="wrap_content"

布局文件,debug后,执行代码


图片.png

发现widthSpecMode 会对应到MeasureSpec.AT_MOST的模式,
发现widthSpecSize = 720px

所以当android:layout_width="wrap_content"时,对应MeasureSpec.AT_MOST的模式,而且系统自己计算的宽度是相当于match_parent的宽度,而我们自定义的view肯定是只要自己的宽度

自己的宽度 等于基本控件宽度+paddingLeft+paddingRight的宽度

图片.png

2: android:layout_width="match_parent", 等同于wrap_content的情况

3: android:layout_width="100dp"准确值

图片.png

当准确值的模式下,debug发现
发现测量模式MeasureSpec.EXACTLY
发现测量宽度widthSpecSize = 200px(布局文件100dp->200px)

所以当这种模式,自定的view的宽度,处理是直接获取自己的宽度,也可以自由点可以准确的值, 这里是取两个当中大的值,防止宽度太小导致view显示不全,这样保证了至少是view完整的大小

图片.png

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容