一、引言
有时候Android传统的页面布局不足以满足我们的需求,常常需要自己定义view,通常继承View,然后重写构造方法以及onDraw等函数,再具体实现自己定义的复杂view。我们知道在给控件赋属性时,通常使用的是android系统自带的属性,比如 android:layout_height="wrap_content",除此之外,我们亦可以自己定义属性,这样在使用的时候我们就可以使用形如 testapp:testSize="15sp"的方式了
二、使用步骤
1.在项目文件res/value下面创建一个attrs.xml文件即自定义属性的声明文件,该文件中包含若干个attr集合,例如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="test">
<attr name="textSize" format="dimension"/>
<attr name="texsColor" format="color"/>
<attr name="type" format="integer"/>
<attr name="background" format="reference"/>
</declare-styleable>
</resources>
其中resource是根标签,可以在里面定义若干个declare-styleable,<declare-styleable name="MyView">中name定义了属性集合的名称,下面可以再自定义多个属性,针对<attr name="testSize" format="dimension "/>来说,其属性的名称为"testSize",format指定了该属性类型为dimension,表示尺寸值。
format还可以指定其他的类型比如;
reference 表示引用,参考某一资源ID
string 表示字符串
color 表示颜色值
dimension 表示尺寸值
boolean 表示布尔值
integer 表示整型值
float 表示浮点值
fraction 表示百分数
enum 表示枚举值
flag 表示位运算
2.在使用到该自定义view的布局文件中键入一行代码,为该自定义属性添加命名空间,名称可以自己定义,在这我定义为testapp,并不要求一定是自定义view的名字,同时使用Eclipse和AS的在此有点区别:
Ecplise:其中com.cx.test为应用程序包名:
xmlns:testapp="http://schemas.android.com/apk/res/com.cx.test"
AS:以前写应用程序包名的方法被AS废弃掉了,直接使用res-auto:
xmlns:testapp="http://schemas.android.com/apk/res-auto"
接下来就可以在布局文件中使用自定义属性啦:
<com.cx.test.MyView
android:layout_width="100dp"
android:layout_height="100dp"
testapp:textColor="#000000"
testapp:textSize="15sp"
testapp:type="1"
/>
3.在自定义view的代码中引入自定义属性,修改构造函数:
AttributeSet与TypedArray:
构造方法中的有个参数叫做AttributeSet(eg: MyView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?首先AttributeSet中的确保存的是该View声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢?
其实看下AttributeSet的方法就明白了,下面看代码:
public class MyView extends View {
private static String TAG=MyView.class.getSimpleName();
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
//使用AttributeSet获取属性
for (int i=0;i<attrs.getAttributeCount();i++){
Log.e(TAG, "MyView: =name=" + attrs.getAttributeName(i) + "=value=" + attrs.getAttributeValue(i));
}
}
}
输出:
E/MyView: MyView: =name=layout_width=value=100.0dip
E/MyView: MyView: =name=layout_height=value=100.0dip
E/MyView: MyView: =name=textSize=value=15.0sp
E/MyView: MyView: =name=textColor=value=#ff000000
E/MyView: MyView: =name=type=value=1
结果果然可以输出自定义view的所有属性,也就是通过AttributeSet可以获得布局文件中定义的所有属性的key和value(还有一些方法,自己去尝试)。
现在关注下一个问题:TypedArray是什么鬼?
我们简单修改下,布局文件中的View的属性:
<com.cx.test.MyView
android:layout_width="100dp"
android:layout_height="100dp"
testapp:textColor="#000000"
testapp:textSize="@dimen/mysize"
testapp:type="1"
testapp:bg="@drawable/aa"
/>
此时输出:
E/MyView: MyView: =name=layout_width=value=100.0dip
E/MyView: MyView: =name=layout_height=value=100.0dip
E/MyView: MyView: =name=textSize=value=@2131230794
E/MyView: MyView: =name=textColor=value=#ff000000
E/MyView: MyView: =name=type=value=1
E/MyView: MyView: =name=bg=value=@2130837504
通过AttributeSet
获取的值,如果是引用都变成了@+数字的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了什么。TypedArray
其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/myszie),如果使用AttributeSet
去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
下面使用TypedArray:
public class MyView extends View {
private static String TAG=MyView.class.getSimpleName();
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
//使用AttributeSet获取属性
// for (int i=0;i<attrs.getAttributeCount();i++){
// Log.e(TAG, "MyView: =name=" + attrs.getAttributeName(i) + "=value=" + attrs.getAttributeValue(i));
// }
//使用TypedArray获取属性
TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.test);
String textSize=ta.getString(R.styleable.test_textSize);
int type=ta.getInteger(R.styleable.test_type,0);
int color=ta.getColor(R.styleable.test_textColor, 0);
Drawable drawable = ta.getDrawable(R.styleable.test_bg);
Log.e(TAG, "MyView: =textSize=" + textSize + "=type=" + type+"=color="+color+"=drawable="+drawable);
ta.recycle();
}
}
输出结果:
E/MyView: MyView: =textSize=20.0sp=type=1=color=-16777216=drawable=android.graphics.drawable.BitmapDrawable@428d6258
可以看出使用TypedArray也可以获取布局文件中使用引用的属性。
通过比较可以看出我们在View的构造方法中,可以通过AttributeSet
去获得自定义属性的值,但是比较麻烦,而TypedArray
可以很方便的便于我们去获取。