一、Android自定义view属性

1.在res/values/styles.xml文件里面声明一个我们自定义的属性:

<resources>
    <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称-->
    <declare-styleable name="CircleView">
        <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)-->
        <attr name="radius" format="dimension"></attr>
        <attr name="circle_color" format="color"></attr>
    </declare-styleable>
</resources>

2.在自定义View中获取对应设置的属性值

public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //即属性集合的标签,在R文件中名称为R.styleable+name
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
        //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称
        //第二个参数为,如果没有设置这个属性,则设置的默认的值
        radius = typedArray.getDimensionPixelOffset(R.styleable.CircleView_radius, 20);
        color = typedArray.getColor(R.styleable.CircleView_circle_color, 000000);
        //最后记得将TypedArray对象回收
        typedArray.recycle();
    }

3.在xml文件中设置属性值

(1)首先需要定义命名空间 xmlns:rc="http://schemas.android.com/apk/res-auto"

(2)设置属性值 rc:radius   rc:circle_color

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:rc="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.ruanchao.todaynews.UserViewActivity">
 
    <com.ruanchao.todaynews.view.CircleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        rc:radius="50dp"
        rc:circle_color="#FF69B4"/>
 
</LinearLayout>

二、Android自定义View

1.onMeasure

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

参数中的widthMeasureSpecheightMeasureSpec包含了两层信息:测量模式和测量尺寸

  (1)测量模式:

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

测量模式

表示意思

UNSPECIFIED

父容器没有对当前View有任何限制,当前View可以任意取尺寸(match_parent)

EXACTLY

当前的尺寸就是当前View应该取的尺寸(xml配置固定值)

AT_MOST

当前尺寸是当前View能取的最大尺寸(wrap_content)

(2)尺寸大小:

int widthSize = MeasureSpec.getSize(widthMeasureSpec);

示例代码:

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" />

2.onDraw 直接在画板Canvas对象上绘制

(1)invalidate方法会执行onDraw过程,只能在UI线程调用

(2)postInvalidate 可以在非UI线程调用,省去了Handler消息调用

(3)RequestLayout 会执行onMeasure,onLayout ,onDraw

三、Android自定义ViewGroup

自定义ViewGruup要经历以下几步:

1、根据各个子View的大小,确定ViewGroup大小(重写onMeasure()方法)

2、在ViewGroup中进行View的摆放(重写onLayout()方法)

示例代码:自定义ViewGroup实现LinearLayout布局

第一步:确定ViewGroup大小

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int with = MeasureSpec.getSize(widthMeasureSpec);
        int height =  MeasureSpec.getSize(heightMeasureSpec);
        int withMode = MeasureSpec.getMode(widthMeasureSpec);
        int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
        if (getChildCount() == 0) {//如果没有子View,当前ViewGroup没有存在的意义,不用占用空间
            setMeasuredDimension(0, 0);
            return;
        }
        if (withMode == MeasureSpec.AT_MOST && heigthMode == MeasureSpec.AT_MOST){
            //高度累加,宽度取最大
            setMeasuredDimension(getMaxChildWidth(),getTotleHeight());
        }else if (heigthMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(with,getTotleHeight());
        }else if (withMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(getMaxChildWidth(),height);
        }
    }
 
    /***
     * 获取子View中宽度最大的值
     */
    private int getMaxChildWidth() {
        int childCount = getChildCount();
        int maxWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getMeasuredWidth() > maxWidth) {
                maxWidth = childView.getMeasuredWidth();
            }
        }
        return maxWidth;
    }
 
    /***
     * 将所有子View的高度相加
     **/
    private int getTotleHeight() {
        int childCount = getChildCount();
        int height = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            height += childView.getMeasuredHeight();
        }
        return height;
    }

第二步:在ViewGroup中进行子View的摆放

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int count = getChildCount();
        int currentHeigth = 0;
        //将子View逐个摆放
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int childHeigth = child.getMeasuredHeight();
            child.layout(left, currentHeigth, right + child.getMeasuredWidth(), currentHeigth + childHeigth);
            currentHeigth += childHeigth;
        }
 
    }

测试xml

<com.rc.studyview.MyViewGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff9900">
 
        <Button
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="btn" />
 
        <Button
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="btn" />
 
        <Button
            android:layout_width="50dp"
            android:layout_height="wrap_content"
 
 
            android:text="btn" />
 
    </com.rc.studyview.MyViewGroup>