自定义View、ViewGroup浅析

自定义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>

    <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称-->
    <declare-styleable name="MyView">
        <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)-->
        <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后,即可通过字段获取在布局文件中设置好的值(第二个参数是获取失败后的默认值)

自定义ViewGroup

自定义ViewGroup的难点在于不仅自身作为View需要控制,作为容器还要将其内部View分配合理。
那么具体该怎么设计:
①首先对于子View,在知道其大小的前提下才能对子View位置进行合理规划
②依据子View大小即ViewGroup功能,决定ViewGroup的大小
③大小都设计好后,需要设计子View的位置摆放。
④摆放方式设置好后,就该实行真正放入子View的工作了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔幻音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值