从0开始学会自定义View(一)

815 阅读4分钟

1、前言

最近发现自己好像并不是很会自定义View这一块捏,像个癌症晚期患者上不去,下不来,就卡那儿了,所以特发此篇文章学习和分享一下自定义View😅。

2、概述

我将自定义View分为三个步骤:

  1. 自定义View属性以及获取
  2. 重写View的onDraw方法
  3. 重写View的onMeasure方法

本篇将说明自定义View属性以及获取

3、自定义View属性以及获取

自定义View属性

首先在选中项目目录中的values文件夹并新建attrs资源文件。如下图:

Ok,创建完成后我们会看到这样的内容:

<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

之后就需要我们在里面申明我们需要的自定义属性了。 属性的申明格式如下:

<attr name="xxxx" format="xxxx"/>
  • name:就是你想要申明属性的名字,到时候获取对应属性依靠的就是这个name。

  • format:申明属性的格式,表示你这个属性它具体是什么,像颜色、字符串或者是其他的类型,但是这个format并不是随意给值的,一下是format的具体可取值的范围:

    formatformat类型
    color表示颜色
    string表示字符串
    boolean表示布尔值
    reference表示引用(如drawable
    dimension表示尺寸
    float表示浮点类型
    integer表示整形
    fraction表示百分数
    enum表示枚举
    flags表示位运算

申明属性的方法有多种,这里给出最常用的两种方法:

  • 普通写法

    <!--背景颜色-->
    <attr name="solid_color" format="color"/>
    <!--文字-->
    <attr name="text" format="string"/>
    <!--文字颜色-->
    <attr name="text_color" format="color"/>
    <!--文字大小-->
    <attr name="text_size" format="dimension"/>
    
  • declare-styleable 标签封装 attr

    <declare-styleable name="MyCustomView">
      <!--背景颜色-->
      <attr name="solid_color" format="color"/>
      <!--文字-->
      <attr name="text" format="string"/>
      <!--文字颜色-->
      <attr name="text_color" format="color"/>
      <!--文字大小-->
      <attr name="text_size" format="dimension"/>
    </declare-styleable>
    

完成之后,我们去创建一个自定义的View,本文类名为MyCustomView继承View,之后补全构造方法即可。

获取自定义属性

首先根据第一步中我们创建的属性在MyCustomView中创建对应的对象:

private int mSolidColor; //背景颜色
private String mText; //文字
private int mTextColor; //文字颜色
private float mTextSize; //文字大小
private Paint mPaint; //画笔
private Rect mTextBound; //接受文字边界

然后写一个获取自定义属性的方法给构造方法调用:

private void obtainAttrs(Context context, AttributeSet attrs) {

}

接着就是最重要的步骤——使用创建TypedArray对象并用其获取到我们创建的属性然后应用到画笔中:

  • 普通写法获取属性:

      int[] attrsKey = new int[]{R.attr.solid_color, R.attr.text, R.attr.text_color, R.attr.text_size};
      TypedArray a = context.obtainStyledAttributes(attrs, attrsKey);
      mSolidColor = a.getColor(0, 0xFF000000);
      mText = a.getString(1);
      mTextColor = a.getColor(2, 0xFFFFFFFF);
      mTextSize = a.getDimension(3, 14F);
      a.recycle();
    

    其中attrsKey代表自定义属性的集合,在context的obtainStyledAttributes方法中依靠这个集合的索引来获取具体的属性

  • 带有declare-styleable标签获取属性:

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
    mSolidColor = a.getColor(R.styleable.MyCustomView_solid_color, 0);
    mText = a.getString(R.styleable.MyCustomView_text);
    mTextColor = a.getColor(R.styleable.MyCustomView_text_color,0xFFFFFFFF);
    mTextSize = a.getDimension(R.styleable.MyCustomView_text_size,sp2px(14));
    a.recycle();
    

    其中MyCustomView是自定义属性中你申明的标签名字,在后续获取具体的属性时传入的参数格式就为“申明的styleable名称_自定义属性名“ 应用到画笔中:

mPaint = new Paint();
mPaint.setTextSize(mTextSize); //设置文字大小
mTextBound = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), mTextBound);//测量文字边界

获取到TypedArray对象之后就可以使用它的方法来获取对应的属性,这些方法一般都需要索引(index以及一个默认值,最后最重要的一点就是在用完TypedArray对象之后一定一定要记得回收

a.recycle();//回收

最后重写onDraw,画一个简单的带背景的文字图形:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(mSolidColor);//背景颜色
    canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);//绘制背景
    mPaint.setColor(mTextColor);//文字颜色
    canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, mPaint);//绘制文字
}

在布局文件引入我们的自定义View:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="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:background="@color/white"
    tools:context=".MainActivity">

    <com.my.mycustomview.MyCustomView
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:solid_color="@color/black"
        app:text="AAA"
        app:text_color="@color/white"
        app:text_size="25sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

效果图如下:

4、更多

复用自定义属性

当我们一个自定义View有许多继承的子类,这些字类有共用的属性时可以选择复用自定义属性(以本文中的MyCustomView举例:

<!--背景颜色-->
<attr name="solid_color" format="color"/>
<!--文字-->
<attr name="text" format="string"/>
<!--文字颜色-->
<attr name="text_color" format="color"/>
<!--文字大小-->
<attr name="text_size" format="dimension"/>

<declare-styleable name="MyCustomView">
    <attr name="solid_color"/>
    <attr name="text"/>
    <attr name="text_color"/>
    <attr name="text_size"/>
</declare-styleable>
<!--另一个View-->
<declare-styleable name="XXXView">
    <attr name="solid_color"/>
    <attr name="text"/>
    <attr name="text_color"/>
    <attr name="text_size"/>
    <!--特有属性-->
    <attr name="xxxx" format="xxxx"/>
</declare-styleable>

这样共用的属性不用重复定义,在取值的时候只需要改变你申明的styleable名字即可:

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.XXXView);

关于attrs

以第3点中的attrs为例子,attr 标签属性会在R类下的attr类中生成16进制地址静态常量

而使用declare-styleable封装的attr会在R类下的styleable类封装attr类下的16进制地址生成int[]数组,并生成下标索引:

结尾

那么到这里这篇内容就结束了,在这篇文章中讲述了自定义View的基本步骤,以及如何自定义属性和如何获取这些属性,关于重写onDraw方法会放到下一章节说明,时间不定,作者懒狗一条🐶,拜拜👋捏!