android自定义属性

     平时在布局文件中布局view的时候经常要设置view的属性比如android:textColor,android:background等等。那么这些属性是怎么在那里定义的,怎么被使用呢。这篇文章就来介绍android属性的定义和使用。并会详细讲解跟自定属性相关的几个重要的类如AttributeSet,TypeArray,TypeValue等。

1.定义属性的一般步骤
1).在res/value新建attrs.xml文件
这里写图片描述
2).在resources根标记下用attr标签定义一个名为myAttr的属性,类型为integer

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <attr name="myAttr" format="integer"/>
</resources>

说明以下,系统在解析时是以定义资源的标签(如上面的attr标签)来区分不同资源的,跟文件前缀名无关,我们一般把定义属性放在attrs的xml文件下只是方便管理,但是该xml文件一定要放在es/value路径下,并且一定要在resources根标签下定义属性。format指定属性的类型,有以下几种属性类型:
integer:整型,
boolean 智能设置true或false,
float 浮点类型,
color :颜色值,以#开头,也可以用@引用一个定义好的颜色,
dimension:尺寸类型,如dp,sp,px等等,也可以用@引用一个定义好的尺寸,
enum:枚举类型,
fraction:百分比,可用nn%后nn%p两种格式设置,
reference引用类型 ,
string字符串类型可以用@引用一个定义好的字符串,
flag 标志类型,我们平时用的如layout_gravity=”left|top”,其中left和top是其中的两个标志
下面会一个个介绍这些类型,以及他们在布局文件中怎么设置,在代码中怎么获取。

2.获取属性的java类
1).AttributeSet:在控件的构造函数中都会看到这个接口,是的,LayoutInflater在对布局文件解析之后就会把对应控件的属性放到这个接口的对象中传给控件的构造函数。但是AttributeSet接口获取的属性值是没有经过转换的(比如说我设置的layout_width=”20dp”,当我们用AttributSet获取layout_width属性是只能拿到“20dp”这个字符串)。看看AttributeSet的一些方法

//返回已设置属性个数
public int getAttributeCount();
//返回指定位置属性名字,如"layout_width"
public String getAttributeName(int index);
//返回指定位置的属性值,如"20dp"
public String getAttributeValue(int index);
//如果指定位置的属性值用@引用了另一资源,方法该资源id,否则返回defaultValue
public int getAttributeResourceValue(int index, int defaultValue);
//如果指定位置的属性值用是bool值,返回该值,否则返回defaultValue
public boolean getAttributeBooleanValue(int index, boolean defaultValue);
//如果指定位置的属性值用是int值,返回该值,否则返回defaultValue
public int getAttributeIntValue(int index, int defaultValue);
//如果指定位置的属性值用是Float值,返回该值,否则返回defaultValue
public float getAttributeFloatValue(int index, float defaultValue);
//方法指定位置的属性id,如上面的myAttr这个属性的id
public int getAttributeNameResource(int index);
...

AttributeSet接口中还有其他getAttributeXXXValue(int index, float defaultValue)方法,基本用法都差不多。由于AttributeSet中提供的方法只能拿到属性的字符串值或者一些基本数据类型的值,当设置的类型和XXX类型不符时便会报错,所以我们在对属性获取的时候不会直接用AttributeSet而是借助另一个类,也就是我们常用的TypedArray。

2).TypedArray:TypedArray内部会对AttributeSet做进一步转换,通过它我可以直接拿到尺寸大小,颜色值,百分值等等(比如layout_width=”20dp”,利用TypedArray内部会根据当前屏幕分辨率计算出20dp对应的像素值,这样我们就不用当心单位的换算了,bing’j),并且当设置的类型和函数返回类型不符时会尝试做转换。用下面的方法可以获取一个TypeArray对象

//Theme类中的方法
/*返回TypedArray 对象,包含目标属性组attrs在当前theme(主题)中设置的值,
参数attrs为目标属性组*/
public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs)

/*返回TypedArray 对象,包含目标属性组attrs在指定的style(样式)中设置的值,
参数attrs为目标属性组,resid为指定style的id*/
public final TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs) ;

/*返回TypedArray 对象,包含目标属性组attrs在指定的AttributeSet中设置的值,
如果目标属性组attrs中的属性在AttributeSet找不到相应值,则到当前theme去找
参数attrs为目标属性组,set为指定的AttributeSet对象*/
public final TypedArray obtainStyledAttributes( AttributeSet set, @StyleableRes int[] attrs) ;

/*返回TypedArray 对象,包含目标属性组attrs在指定的AttributeSet中设置的值。
参数attrs为目标属性组,set为指定的AttributeSet对象。
defStyleAttr为默认属性id,如果AttributeSet中找不到目标属性值,则在该属性所设置的style去找,传0代表不是该值。
defStyleRes为默认style的id,如过AttributeSet,defStyleAttr,和当前主题中都没有目标属性值,则使用该style设定的属性值*/
public final TypedArray obtainStyledAttributes( AttributeSet set, @StyleableRes int[] attrs,@AttrRes int defStyleAttr,@StyleRes int defStyleRes) ;

//Resources类中方法
/*返回TypedArray 对象,包含目标属性组attrs在指定的AttributeSet中设置的值,
和theme类的方法的区别是如果在AttributeSet找不到目标属性不会使用theme设定值 */
public TypedArray obtainAttributes(AttributeSet set, int[] attrs)

3.各种属性类型的设置并通过TypedArray获取
1).定义好下面这些属性

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

    <declare-styleable name="CustomView">

        <attr name="integer_attr" format="integer"/>
        <attr name="float_attr" format="float"/>
        <attr name="color_attr" format="color"/>
        <attr name="dimension_attr" format="dimension"/>
        <!-- 为了演示百分号的两种形式-->
        <attr name="fraction_attr1" format="fraction"/>
        <attr name="fraction_attr2" format="fraction"/>
        <attr name="reference_attr" format="reference"/>
        <!-- 这里定义四种enum,供布局文件设置-->
        <attr name="enum_attr" format="enum">
            <enum name="enum_left" value="1" />
            <enum name="enum_top" value="2" />
            <enum name="enum_right" value="3" />
            <enum name="enum_bottom" value="4" />
        </attr>
        <attr name="boolean_attr" format="boolean"/>

        <!-- 这里定义五个flag,供布局文件设置,设置是可多选,并用‘|’分开-->
        <attr name="flag_attr">
            <flag name="flag_left" value="0x01"/>
            <flag name="flag_top" value="0x02"/>
            <flag name="flag_right" value="0x04"/>
            <flag name="flag_bottom" value="0x08"/>
            <flag name="flag_center" value="0xf0"/>
        </attr>
        <attr name="string_attr" format="string"/>

    </declare-styleable>

</resources>

上面在declare-styleable标签对属性分组,在编译的时候会在R.java文件下的styleable内部类中生成一个名为CustomView的静态常数组,该数组内存放的是declare-styleable标签下的各个属性的id值,该数组也要传给上文说到的obtainStyledAttributes方法的目标属性数组。同时,在styleable内部类中还会生成CustomView_[属性名]常量,常量的值就是对应属性id在CustomView数组的索引,下面是R.java的源码

public final class R {

    //以string_attr属性为例,这是string_attr属性id值
    public static final int string_attr=0x7f01004a;

    //styleable内部类
    public static final class styleable{

        //0x7f01004a在CustomView的位置是10
        public static final int[] CustomView = {
            0x7f010040, 0x7f010041, 0x7f010042, 0x7f010043,
            0x7f010044, 0x7f010045, 0x7f010046, 0x7f010047,
            0x7f010048, 0x7f010049, 0x7f01004a
        };
        //CustomView_string_attr常量也为10
        public static final int CustomView_string_attr = 10;
    }
}

2).在布局中设置属性值

<?xml version="1.0" encoding="utf-8"?>
<!--要使用自定义属性一定要在根该布局的根视图中
声明命名空间xmlns:app="http://schemas.android.com/apk/res-auto"-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <!--CustomView是一个自定义view,设置属性是用app:即上面的命名空间xmlns:app -->
    <com.example.administrator.myapplication.CustomView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:integer_attr="10"
        app:float_attr="10.5"
        app:boolean_attr="true"
        app:color_attr="#ffff00"
        app:dimension_attr="50dp"
        app:fraction_attr1="50%"
        app:fraction_attr2="50%p"
        app:reference_attr="@string/app_name"
        app:enum_attr="enum_left"
        app:flag_attr="flag_left|flag_top"
        app:string_attr="test_string"/>

</LinearLayout>

3).在java中获取属性值

public class CustomView extends View{
    public CustomView(Context context) {
        super(context);
        init(context, null, 0);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

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

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        //调用obtainStyledAttributes获取TypedArray对象,context对象的obtainStyledAttributes函数实在也是
        // 调用了Theme对象的obtainStyledAttributes,这里面的目标属性在布局文件都设置的,所以在AttributeSet中是可以获取到的
        //@StyleableRes int[] attrs传的正好是上面提到的在R.java文件中的CustomView属性组
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView,defStyleAttr,0);

        //getInteger()获取整型值,第一个参是上面目标属性组低索引(索引也是在R.java中的常量,下同),第二个是默认值
        int integer_attr = a.getInt(R.styleable.CustomView_integer_attr, 0);
        Log.d("test", "integer_attr = " + integer_attr);

        //getBoolean()获取boolean值,第一个参是上面目标属性组低索引,第二个是默认值
        boolean boolean_attr = a.getBoolean(R.styleable.CustomView_boolean_attr, false);
        Log.d("test", "boolean_attr = " + boolean_attr);

        //getFloat()获取float值,第一个参是上面目标属性组低索引,第二个是默认值
        float float_attr = a.getFloat(R.styleable.CustomView_float_attr, 0.0f);
        Log.d("test", "float_attr = " + float_attr);

        /*getInteger()获取百分比值,第一个参是上面目标属性组低索引,
        * 分为两种情况:
        * 1.当布局文件中的属性设置为nn%时,第三个参无效,返回值 = 属性设置百分比 × 第二个参数
        * 2.当布局文件中的属性设置为nn%p时,第二个参无效,返回值 = 属性设置百分比 × 第三个参数
        * */
        float fraction_attr1 = a.getFraction(R.styleable.CustomView_fraction_attr1, 1, 2, 60);
        Log.d("test", "fraction_attr1 = " + fraction_attr1);
        float fraction_attr2 = a.getFraction(R.styleable.CustomView_fraction_attr2, 1,2,60);
        Log.d("test", "fraction_attr2 = " + fraction_attr2);

        //getString()获取字符串值
        String string_attr = a.getString(R.styleable.CustomView_string_attr);
        Log.d("test", "string_attr = " + string_attr);

        //getInteger()获取Dimension值,第一个参是上面目标属性组低索引,第二个是默认值,返回的是已计算好的以像素为单位的浮点值
        float dimension_attr = a.getDimension(R.styleable.CustomView_dimension_attr, 0);
        Log.d("test", "dimension_attr = " + dimension_attr);

        //getInteger()获取整型值,第一个参是上面目标属性组低索引,第二个是默认值,返回的是已计算好的颜色值
        int color_attr = a.getColor(R.styleable.CustomView_color_attr, 0);
        Log.d("test", "color_attr = " + color_attr);

        /*对于reference类型的属性值,可以用getResourceId拿到对应资源id,由于string,dimensions,color也可
        以在布局文件中设置引用资源,所以reference类型的属性值也可用getDimension,getString,getColor等方法获取。
        当然,如果string,dimensions,color类型的属性用@在布局文件中设置引用资源,用getResourceId也拿得到对应资源id
        */
        int reference_attr = a.getResourceId(R.styleable.CustomView_reference_attr, 0);
        Log.d("test", "reference_attr = " + reference_attr);

        /*对于enum类型的值的获取要根据实际enum的类型的来定,我上面定义属性的时候value值分别为"1","2","3","4".
        所以可以用getInt,getFloat,getString,方法来获取,这里用到了另一获取整型的方法getInteger,
        他跟getInt的区别在于:如果对应所以的属性值如果不是整型,则会抛异常,不会像getInt那样做转换
         */
        int enum_attr = a.getInteger(R.styleable.CustomView_enum_attr, 0);
        Log.d("test", "enum_attr = " + enum_attr);

        /*获取flag类型值,flag类型在布局中设为"flag_left|flag_top",结果就是0x02 | 0x01为3,要判断是否设置了某个flag,跟该flag进行位与即可*/
        int flag_attr = a.getInteger(R.styleable.CustomView_flag_attr, 0);
        Log.d("test", "flag_attr = " + flag_attr);

        //调用recycle释放TypedArray内存
        a.recycle();

    }
}

4).打印输出
这里写图片描述
     注意一下,属性的类型和TypeArray的getXXX()并不一定要一一对应,getXXX()会对布局文件中设置的值进行转换(如可以包#开头的值转换为颜色值或int值),如果无法转换(如当用getFraction获取非百分数时)会报错,属性的类型只是限定了在布局文件中的设置方式(如Integer类型不能设置成”5.6”,”20dp”)。另外在TypeArray使用结束后一定要调recycle以师范释放内存
     以上就是自定义属性的全部内容,当然这里知识简介的获取了属性而没有使用,后续我会上传几个简单的自定义的到csdn中一供参考

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值