关于attr所需要知道的事

概述

在我们自定义view的时候,一般会用到自定义属性,来做view的属性配置(比如:显示控制,dis/enable状态等)。
自定义属性的步骤如下

  1. 创建一个attr.xml,在styleable标签里自定义所需要的属性
  2. 在布局xml文件中使用自定义view,配置自定义属性的值
  3. 在自定义view中,通过TypedArray对象,获取自定义属性的值,然后控制view的行为

这篇文章就讲一讲,以上3个步骤中容易忽略的事情,并且以自定义导航栏为例来做讲解。

AttributeSet

前提条件:

http://schemas.android.com/apk/res-auto 是自定义属性值的namespace,也就是要在布局xml文件中使用自定义属性标签时需要声明的namespace
http://schemas.android.com/apk/res/android 是系统默认的namespace,以android:开头

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:navibar="http://schemas.android.com/apk/res-auto"
    ...
    <com.example.hly.attrdemo.NavigationBar
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        navibar:leftText="back"
        navibar:leftImage="@drawable/vr_back"/>
</RelativeLayout>

在我们的自定义view的构造方法中有一个参数是AttributeSet。
从名字上看是属性值的一个set集合,那在xml中设置了自定义属性值后,能通过AttributeSet获取配置的值,不通过TypedArray吗?试一试先。

int attrsCount = attrs.getAttributeCount();
Log.i(TAG, "AttributeSet size :" + attrsCount);
for (int i = 0; i < attrsCount; i++) {
      String name = attrs.getAttributeName(i);
      String value = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", name);
      if (TextUtils.isEmpty(value)) {
            value = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", name);
      }
      Log.i(TAG, "Attribute name :" + name + " Attribute value:" + value );
}

最后的输出结果是

09-02 14:48:26.455 I/NavigationBar(13763): Attribute name :layout_width Attribute value:-2
09-02 14:48:26.455 I/NavigationBar(13763): Attribute name :layout_height Attribute value:35.0dip
09-02 14:48:26.455 I/NavigationBar(13763): Attribute name :leftText Attribute value:back
09-02 14:48:26.455 I/NavigationBar(13763): Attribute name :leftImage Attribute value:@2130837580

前面3行都很正常,但是@2130837580是个什么鬼?
熟悉的童鞋一眼就能看出来,这其实是一个引用ID值,如果要获得引用的具体值还需要做额外的处理

 if (name.equals("leftImage")) {
      int id = attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "leftImage", -1);
      Drawable drawable = context.getResources().getDrawable(id);
      Log.i(TAG, "drawable:" + drawable);
}

这里会打印出drawable对象.
如果使用TypedArray就可以很方便拿到具体的值,无论是引用还是硬编码。

TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.NaviBar);
Drawable leftDrawable = typedArray.getDrawable(R.styleable.NaviBar_leftImage);

所以,通过AttributeSet,不仅可以获得android namespace的值,也能获得自定义属性的值,不过稍微有点麻烦,这也就是为什么一般都会使用TypedArray来

declare-styleable

其中flag和enum是相对来说比较少用的标签,这里大致讲下怎么使用

<declare-styleable name="NaviBar">
        <attr name="flagTest">
            <flag name="flag0" value="1" />
            <flag name="flag1" value="2" />
        </attr>

        <attr name="enumTest">
            <enum name="enum0" value="0" />
            <enum name="enum1" value="1" />
        </attr>
 </declare-styleable>

配置自定义属性
因为是enum类型的,所以这里只能是单一的值

navibar:flagTest="flag0|flag1"
navibar:enumTest="enum0"

获取值

int flagValue = typedArray.getInt(R.styleable.NaviBar_flagTest, -1);
int enumValue = typedArray.getInt(R.styleable.NaviBar_enumTest, -1);
Log.i(TAG, "Attribute flagValue :" + flagValue + " enumValue:" + enumValue);

会打印出如下log

09-02 16:22:25.715 I/NavigationBar(15186): Attribute flagValue :3 enumValue:0

可以看出flagValue的值就是flag0+flag1的值

demo地址

github

推荐阅读更多精彩内容