自定义View浅析

自定义View过程

自定义View关键点:onMeasure()、onDraw()

onMeasure

该函数目的是测量View宽高属性,虽然在xml中制定了View宽高,但当设置为wrap_contentmatch_parent时,此处获取宽高就需要由onMeasure()处理。

举例:
实现一个宽采用match_parent,而高为100dp的正方形。
在不重写方法情况下,上述长宽通常是不同的,所以就需要在测量函数中调整长宽长度。

 private int getMySize(int defaultSize, int measureSpec) {
        int mySize = defaultSize;

        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size
                //我们将大小取最大值,你也可以取其他值
                mySize = size;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它
                mySize = size;
                break;
            }
        }
        return mySize;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMySize(100, widthMeasureSpec);
        int height = getMySize(100, heightMeasureSpec);

		//进行正方形长宽判断设置
        if (width < height) {
            height = width;
        } else {
            width = height;
        }

        setMeasuredDimension(width, height);
}

布局:

  <com.hc.studyview.MyView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#ff0000" />

方法内部分参数名词解析:

onMeasure(int widthMeasureSpec, int heightMeasureSpec)
其中两个参数分别包含了宽度+测量模式,高度+测量模式
存放方式:一个int数据占32bit,前两个bit用于存放测量模式(只有三个模式,2bit足够存放),后30bit存放尺寸数据
其内容用内置类MeasureSpec可以直接获取:

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);

测量模式:
match_parent对应EXACTLY:当前尺寸即View应该用的尺寸(即父View剩余空间尺寸)
wrap_content对应AT_MOST:当前尺寸是View可以取的最大尺寸
用户定的尺寸对应EXACTLY:指定大小,不用再干涉
还有一个测量模式UNSPECIFIED:对当前View没有限制

注意事项

上述控件需要放在LinearLayout中,不然setMeasuredDimension(width, height);将无效,而此句是确定长宽的重要部分

onDraw

在上述正方形View绘画一个圆形:

	@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int r = getMeasuredWidth() / 2;
        int x = r;
        int y = r;

        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        canvas.drawCircle(x, y, r, paint);
    }

注:canvas.drawCircle(x, y, r, paint);这一步前两个x,y是相对于当前View的位置,有文章内采用int centerX = getLeft() + r;这样获取出来的圆心位置是相对于父View的布局,即当View在左上角,显示正常(起始点就在父View0,0处),而换位置后,球就会消失,即小球不会跟着View移动

自定义布局属性

可以通过自定义布局属性去在布局里给控件xml文件添加字段。
styles.xml
该文件定义具体字段
res下values下建立该文件

<resources>

    
    <declare-styleable name="MyView">
        
        <attr name="default_size" format="dimension" />
    declare-styleable>
resources>

此时在布局文件中,控件就可以添加对应default_size属性:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:proper="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <com.example.view.MyView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#ff0000"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        proper:default_size="100dp"/>


LinearLayout>

注意头段xmlns:proper用于将自定义属性与其余自带分别出来,即在控件中直接proper:default_size="100dp"即可

通过上述设置,就可以在View中依据添加的字段获取其值:

public class MyView extends View {
    private int defaultSize;

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);

        defaultSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100);

        a.recycle();
    }

解析:
context.obtainStyledAttributes(attrs, R.styleable.MyView);这里第二个参数就是在styles.xml里设置的属性集合名
defaultSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100);经过上述获得TypedArray后,即可通过字段获取在布局文件中设置好的值(第二个参数是获取失败后的默认值)

你可能感兴趣的