概述
在我们自定义view的时候,一般会用到自定义属性,来做view的属性配置(比如:显示控制,dis/enable状态等)。
自定义属性的步骤如下
- 创建一个attr.xml,在styleable标签里自定义所需要的属性
- 在布局xml文件中使用自定义view,配置自定义属性的值
- 在自定义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的值