天天看点

Android自定义属性需要注意的细节

文章已经迁移到简书, 请移步简书进行查看关于Android自定义属性你可能不知道的细节

关于Android自定义属性网上已经有很多大神都已经详细的讲解过了. 关于如何使用自定义属性大家可以参考 鸿洋_大神的这篇文章Android自定义属性Android 深入理解Android中的自定义属性 , 在此小弟再补充一些在自定义属性时候应该注意的一些细节.

  1. 如何定义可被多个自定义控件使用的属性
  2. 如何使用Android已经定义好的属性
  3. 各种类型的属性如何定义(文字样式 , 背景)等.
  4. 如何读取各种类型的自定义属性

1. 如何定义可被多个自定义控件使用的属性

<declare-styleable name="CustomView">
        <attr name="attr1" format="string"/>
</declare-styleable>
           

假设我们有如上面这段代码 , 然后我们要在另外一个自定义控件中使用名称为” attr1”的自定义属性那么编译器会抛错(错误如下)

Error:Execution failed for task':CustomAttributes:mergeDebugResources'.
> /Users/xxx/WorkSpace/IdeaProjects/MyApplication/CustomAttributes/src/main/res/values/attrs.xml: Error: Found item Attr/attr1 more than one time
           

那么我们如何定义一个能被多个自定义控件使用的属性呢

我们知道在java类中定义一个被多个实例方法共享的变量—将变量定义在方法体外面,因此我们在自定义属性时也可以使用同样的方法将属性定义在declare-styleable标签之外 , 这样自定义属性就可以被多个自定义控件使用了.

<attr name="attr1" format="string"/>
           

2.如何使用Android已经定义好的属性

Android系统已经为我们定义好了很多属性名称,所有已

xmlns:android="http://schemas.android.com/apk/res/android"

命名空间,开头的属性都是Android已经定义好的属性 , 在使用的时候只需要直接使用即可, 但是应该注意的是, 如果这个属性在被继承类中已经使用过那么建议不要使用, 如

TextView

android:text

属性就是已经被使用的属性, 所以在自定义控件时候如果继承自

TextView

那么这个属性就不应该被使用.

<declare-styleable name="CustomView">
        <!--我们可以按下面三行这样来为自定义控件添加,系统已经声明过的属性-->
        <attr name="android:divider"/>
        <attr name="android:dividerPadding"/>
        <attr name="android:showDividers"/>
</declare-styleable>
           

各种类型的属性如何定义

Android中的属性是如何定义的, 在使用自定义属性的时候我们应该了解Android是如何自定义属性的,.

比如我们定义一个

string

类型的属性

text

那么我们可以使用

<attr name="text" format="string"/>

来定义 .

但是像

textColor

这样的复杂属性 既可以使用

#333333

又可以使用

drawable

类型的值, 这样的属性又改如何定义,

#333333

属于

color

类型 而

drawable

属于

reference

类型 ,这就需要定义个属性使用两种类型的值, 因此我们可以通过

color| reference

来定义如:

<attr name="textColor" format="color|reference"/>

. 其他定义请参考以下代码及注释.

<!--定义文字颜色,这里和系统的TextColor定义相同,注意和背景色读取方式的区别-->
    <attr name="CustomTextColor" format="color|reference"/>
    <!--定义控件背景色,和系统的Background定义相同,注意和文字颜色读取的区别-->
    <attr name="CustomBackground" format="color|reference"/>
           

4.如何读取各种类型的自定义属性

第一步是获取 declare-styleable

第二步是在获取的集合中取出每个属性值

第三部将读取的属性值应用到自定义

view

自定义属性分有

string,integer,color,reference

等简单类型, 也有

color|reference

等这种复合类型 , 简单类型的读取只需使用

a.getString(R.styleable.CustomView_CustomString)

的方式读取, 复合属性的读取需要按区别对待, 如对于字体和背景同样是

color|reference

类型,但是他们的读取方法也有区别 , 如

textColor

一般使用

ColorStateList

类型

background

一般使用

Drawable

类型. 因此需要使用不同的接口来读取 .

ColorStateList

类型使用

getColorStateList(R.styleable.xxx)

接口,

Drawable

类型使用

getDrawable(R.styleable.xxx)

接口. 具体介绍请参考下面代码.

public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        final Resources res = getResources();
        // 这里能读取的属性是在<declare-styleable name="CustomView">定义的属性
        // 这一行获取 [样式文件]和[控件属性] 里面定义的 <declare-styleable name="CustomView"> 标签中的属性集合
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, R.attr.CustomStyle, );
        // 读取资源文件类型的属性
        resource = a.getResourceId(R.styleable.CustomView_CustomResource, );
        // 读取color类型的属性(#333333)
        color = a.getColor(R.styleable.CustomView_CustomColor, res.getColor(R.color.colorPrimary));
        // 读取文字颜色属性(#333333或者selector类型的drawable资源)
        textColor = a.getColorStateList(R.styleable.CustomView_CustomTextColor);
        // 读取背景(#333333或者drawable类型资源)注意与文字颜色的区别
        drawable = a.getDrawable(R.styleable.CustomView_CustomBackground);
        // 读取int类型属性
        height = a.getInteger(R.styleable.CustomView_CustomHeight, );
        // 读取dp类型的属性,读出来的值已经转换为px
        width = a.getDimensionPixelSize(R.styleable.CustomView_CustomWidth, );
        // 读取字符串类型的属性
        text = a.getString(R.styleable.CustomView_CustomString);
        // 读取枚举类型的属性
        enumValue = a.getInt(R.styleable.CustomView_CustomEnum, );

    }
           

最后附上所有代码

1. attr.xml

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

    <!--给自定义控件定义一个样式,在styles.xml里面可以通过这个样式对控件设置样式-->
    <attr name="CustomStyle" format="reference"/>

    <!--对应一个引用如drawable,color等-->
    <attr name="CustomResource" format="reference"/>
    <!--定义一个颜色属性格式为#333333-->
    <attr name="CustomColor" format="color"/>
    <!--定义文字颜色,这里和系统的TextColor定义相同,注意和背景色读取方式的区别-->
    <attr name="CustomTextColor" format="color|reference"/>
    <!--定义控件背景色,和系统的Background定义相同,注意和文字颜色读取的区别-->
    <attr name="CustomBackground" format="color|reference"/>
    <!--定义控件的高度,单位px-->
    <attr name="CustomHeight" format="integer"/>
    <!--定义控件宽度,单位dp-->
    <attr name="CustomWidth" format="dimension"/>
    <!--定义一个字符串属性-->
    <attr name="CustomString" format="string"/>
    <!--定义一个枚举类型属性-->
    <attr name="CustomEnum" format="enum">
        <enum name="horizontal" value="0"/>
        <enum name="vertical" value="1"/>
    </attr>
    <!--其他属性定义参考以上-->

    <!--自定义控件需要用到的属性-->
    <declare-styleable name="CustomView">
        <!--我们可以按下面三行这样来为自定义控件添加,系统已经声明过的属性-->
        <attr name="android:divider"/>
        <attr name="android:dividerPadding"/>
        <attr name="android:showDividers"/>

        <!--在declare-styleable标签中定义的属性不能在另一个declare-styleable标签中再次使用-->
        <attr name="OnlyOne" format="boolean"/>
        <!--在declare-styleable标签中定义的属性可以被多次使用-->
        <attr name="CustomResource"/>
        <attr name="CustomColor"/>
        <attr name="CustomTextColor"/>
        <attr name="CustomBackground"/>
        <attr name="CustomHeight"/>
        <attr name="CustomWidth"/>
        <attr name="CustomString"/>
        <attr name="CustomEnum"/>

    </declare-styleable>

    <declare-styleable name="CustomView1">

        <!--在declare-styleable标签中定义的属性不能在另一个declare-styleable标签中再次使用-->
        <!--<attr name="OnlyOne" format="boolean"/>-->
        <!--在declare-styleable标签中定义的属性可以被多次使用-->
        <attr name="CustomColor"/>

    </declare-styleable>

</resources>
           

2. CustomView.java

package com.example.liubin.customattributes;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

public class CustomView extends View {
    private final int color;
    private final ColorStateList textColor;
    private final Drawable drawable;
    private final int height;
    private final int width;
    private final String text;
    private final int enumValue;
    int resource;

    public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        final Resources res = getResources();
        // 这里能读取的属性是在<declare-styleable name="CustomView">定义的属性
        // 这一行获取 [样式文件]和[控件属性] 里面定义的 <declare-styleable name="CustomView"> 标签中的属性集合
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, R.attr.CustomStyle, );
        // 读取资源文件类型的属性
        resource = a.getResourceId(R.styleable.CustomView_CustomResource, );
        // 读取color类型的属性(#333333)
        color = a.getColor(R.styleable.CustomView_CustomColor, res.getColor(R.color.colorPrimary));
        // 读取文字颜色属性(#333333或者selector类型的drawable资源)
        textColor = a.getColorStateList(R.styleable.CustomView_CustomTextColor);
        // 读取背景(#333333或者drawable类型资源)注意与文字颜色的区别
        drawable = a.getDrawable(R.styleable.CustomView_CustomBackground);
        // 读取int类型属性
        height = a.getInteger(R.styleable.CustomView_CustomHeight, );
        // 读取dp类型的属性,读出来的值已经转换为px
        width = a.getDimensionPixelSize(R.styleable.CustomView_CustomWidth, );
        // 读取字符串类型的属性
        text = a.getString(R.styleable.CustomView_CustomString);
        // 读取枚举类型的属性
        enumValue = a.getInt(R.styleable.CustomView_CustomEnum, );

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(color);
        Paint paint = new Paint();
        paint.setColor(getResources().getColor(R.color.colorPrimary));
        canvas.drawText(text, , , paint);
    }
}
           

3. text_color.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 按下 -->
    <item android:color="#333333" android:state_pressed="true"/>
    <!-- 获取焦点 -->
    <item android:color="#ff0000" android:state_focused="true"/>
    <!-- 选中 -->
    <item android:color="#ff0000" android:state_selected="true"/>
    <!-- 默认 -->
    <item android:color="#333333"/>

</selector>
           

4. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.liubin.customattributes.MainActivity">

    <com.example.liubin.customattributes.CustomView
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:CustomColor="#ff0000"
        app:CustomString="我的文字"
        app:CustomTextColor="@drawable/text_color"/>
</RelativeLayout>
           

5. MainActivity.java

package com.example.liubin.customattributes;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}