Android之自定义View(二)

本文详细介绍自定义View的步骤,包括自定义属性、构造方法、onMeasure、onDraw等方法的重写,以及如何处理wrap_content等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义View/ViewGroup的一般步骤:

    1)  自定义属性;
    2)  选择和设置构造方法;
    3)  重写onMeasure()方法;
    4)  重写onDraw()方法;
    5)  重写onLayout()方法;

    6)  重写其他事件的方法(滑动监听等)。

自定义属性

    在values目录下面创建自定义属性的XML,比如attrs.xml,文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleView">
        <attr name="circle_color" format="color" />
    </declare-styleable>
</resources>

    这里声明了一个自定义属性的集合叫“CircleView”,在这个集合里面可以有很多自定义属性,这里只定义一个格式为“circle_color”的属性,,格式是color,当然还有其他自定义属性的格式,比如reference是指资源id,dimenson是指尺寸等,常用的format格式类型有:

    1)  string:字符串类型;
    2)  integer:整数类型;
    3)  float:浮点型;
    4)  dimension:尺寸,后面必须跟dp、dip、px、sp等单位;
    5)  Boolean:布尔值;
    6)  reference:引用类型,传入的是某一资源的ID,必须以“@”符号开头;
    7)  color:颜色,必须是“#”符号开头;
    8)  fraction:百分比,必须是“%”符号结尾;

    9)  enum:枚举类型

选择和设置构造方法

    自定义View有三个构造方法,它们的作用是不同的。

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

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

第一个是new 一个对象的时候调用。

第二个是在XML文件声明的时候调用。

系统默认只会调用前两个构造函数,至于第三个构造函数的调用,通常是我们自己在构造函数中主动调用的。

在第三个构造函数中解析自定义属性的值并做相应处理,这里我们解析circle_color这个属性的值,并初始化画笔的颜色,代码如下:

    private int mColor = Color.WHITE;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        init();
    }

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

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

首先加载自定义属性集合CircleView,接着解析CircleView属性集合中的circle_color属性,id为R.styleable.circleView_circle_color,如果使用中没有指定这个属性,那么默认的颜色为红色。在布局文件中使用自定义属性,如下所示:

<?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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="#ffffff"
    android:orientation="vertical"
    tools:context="com.gomez.mycircleview.MainActivity">

    <com.gomez.mycircleview.CircleView
        android:id="@+id/circleView1"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_margin="20dp"
        android:padding="20dp"
        app:circle_color="@color/colorPrimary"
        android:background="#000000"/>
</LinearLayout>

重写onMeasure方法

    这里我们要处理wrap_content的问题,不作处理的话,wrap_content的效果就等于match_parent得效果,这样View就达不到我们预期的效果,代码如下:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 200);
        }
    }

    上面代码中,我们只需给View指定一个默认的内部宽/高(200dp),并在wrap_content时设置此宽/高。

重写onDraw方法

    这里我们画一个圆效果的自定义View,以自己的中心点以宽/高的最小值为直径画一个实心圆,代码如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
    }

因为是直接继承View的自定义View,我们还要处理padding的问题,只要在绘制是把padding考虑进去即可,代码修改如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int paddingLeft = getPaddingLeft();
        final int paddingRight = getPaddingRight();
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingBottom - paddingTop;
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(getPaddingLeft() + width / 2, getPaddingTop() + height / 2, radius, mPaint);
    }

    

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值