美文网首页
自定义View之onMeasure

自定义View之onMeasure

作者: Jowney | 来源:发表于2018-10-14 23:59 被阅读17次

前言:->

onMeasure设置自身的大小,onLayout设置子控件的位置。


自定义View时需要重写的方法

一、解释onMeasure

为了更好的理解onMeasure()的作用,可以先从一个简单的Sample入手。首先,我们写一个自定义View,直接调用系统默认的onMeasure函数,看看会是怎样的现象:

package com.jowney.viewlearn;

/**
 * Created by Jowney on 2018/10/10.
 */

public class CustomView extends View {
    private static final String TAG = "CustomView";

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG, "CustomView: 我来了");
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }


}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:background="@color/colorPrimary"
    tools:context=".MainActivity">

  <com.jowney.viewlearn.CustomView
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:background="@color/colorAccent"
      />

</LinearLayout>

1、父控件使用match_parent,CustomView的layout_width、layout_height为100dp,如图所示:


在该实验中我打印了CustomView的宽高,代码如下:
public class CustomView extends View {
    private static final String TAG = "CustomView";

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG, "CustomView: 我来了");
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        Log.i(TAG, "onMeasure:  宽:"+width+"  高:"+height);
    }

}
打印结果: onMeasure:  宽:263  高:263   (单位是px)

看到这个结果后我会有如下几个疑问:

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法什么时候被调用的,他的参数是什么意思?
  • 在布局文件里面我设置view的宽高是100dp啊!怎么系统计算的结果是263px?
  • 不是说onMeasure方法是用来测量自定义View大小的吗?可是onMeasure方法中并没有具体实现什么啊!怎么就测量出结果了?

解释如下:

  • 第一个问题是,当创建一个View(执行构造方法)的时候并没有测量该View的大小,只有将该view放入一个容器(父控件)中的时候才需要测量,而这个测量方法是父控件主动去调用的。当控件的父控件要放置该控件的时候,父控件会调用子控件的onMeasure方法询问子控件:“你有多大的尺寸,我要给你多大的地方才能容纳你?”,然后传入两个参数(widthMeasureSpec和heightMeasureSpec),这两个参数就是父控件告诉子控件可获得的空间以及关于这个空间的约束条件,子控件拿着这些条件就能正确的测量自身的宽高了。
    widthMeasureSpec和heightMeasureSpec参数其实就是此自定义View的父控件(此处是LinearLayout)根据xml文件中自定义控的layout_height和layout_width设置的值得出的宽和高像素,并加入模式通过MeasureSpec类计算得出的widthMeasureSpec和heightMeasureSpec参数,传入我们重写的OnMeasure中,其实就是父控件已经知道了此自定义view的宽和高,传过来供开发者参考修改。
    所以widthMeasureSpec和heightMeasureSpec 是一个32位int值。高2位 代表SpecMode,低30为代表SpecSize 。
        //通过如下方法可以获取到真正的宽高像素
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        //通过如下方法可以获取到模式
        int modeWidth= MeasureSpec.getMode(widthMeasureSpec);
        int heightHeight =MeasureSpec.getMode(heightMeasureSpec);

问题又来了这个模式到底是个什么东西?如下图


父控件是根据layout_height、layout_width来判断对子控件的约束条件。

  • 第二个问题是,涉及到 像素、像素密度、独立像素密度(后续补充......)

  • 第三个问题是,注意


  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       
    }

当父控件填充子控件的时候会主动调用onMeasure方法,并根据子控件的约束条件计算得出widthMeasureSpec,heightMeasureSpec。注意super关键字,虽然我们在自定义的控件里面什么也没实现,但是我们继承的View类里面有实现啊!

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }



  public static int getDefaultSize(int size, int measureSpec) {
        //这size主要是为了防止UNSPECIFIED,模式而设置的值
        //当自定义控件的约束条件layout_height、layout_wight为负数时,就是UNSPECIFIED模式
        int result = size;
        //获取模式和宽高值
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
      //我们在这里有模式选择
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }




protected int getSuggestedMinimumWidth () {
    return (mBackground == null) ? mMinWidth : max(mMinWidth , mBackground.getMinimumWidth());
}

2、 父控件使用match_parent,CustomView使用match_parent
结果如下图,很明显CustomView将LinearLayout填充满了,因为LinearLayout背景色时蓝色。


3、父控件使用match_parent,CustomView使用wrap_content
结果如下图,CustomView依然将LinearLayout填充满了。


相关文章

网友评论

      本文标题:自定义View之onMeasure

      本文链接:https://www.haomeiwen.com/subject/vygwaftx.html