TypeArray 用来简化资源类型判断,declare-styleable 用来生成资源 ID 数组和对应的索引值
自定义属性的声明文件(values/attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="test">
<attr name="text" format="string" />
<attr name="testAttr" format="integer" />
</declare-styleable>
</resources>
自定义 View
public class MyTextView extends View {
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
String text = ta.getString(R.styleable.test_testAttr);
int textAttr = ta.getInteger(R.styleable.test_text, -1);
ta.recycle();
}
}
Xml 布局文件
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.test.MyTextView
android:layout_width="100dp"
android:layout_height="200dp"
zhy:testAttr="520"
zhy:text="@string/hello_world" />
</RelativeLayout>
代码看不懂不要紧,后面慢慢分析,我们先说一下 View 构造方法中的 AttributeSet 参数,首先AttributeSet 中
保存的是 View 在布局文件中声明的所有属性,而且我们的确可以通过它去获取属性键值对,下面我们修改一下 MyTextView 中的代码:
public class MyTextView extends View {
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String attrName = attrs.getAttributeName(i);
String attrVal = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal);
}
}
}
运行结果为:
MyTextView(4692): attrName = layout_width , attrVal = @2131165234
MyTextView(4692): attrName = layout_height , attrVal = @2131165235
MyTextView(4692): attrName = testAttr , attrVal = 520
MyTextView(4692): attrName = text , attrVal = @2131361809
通过结果我们发现,打印出来的 text 属性是以 @ 开头的,我们改一下代码
public class CustomView extends View {
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
if (attrs == null) return;
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
// if(attrs.getAttributeName(i).equals("text")) {
if (attrs.getAttributeNameResource(i) == R.attr.text) {
int resValue = attrs.getAttributeResourceValue(i, -1);
if (-1 != resValue) {
Log.e("offer", getResources().getString(resValue));
} else {
Log.e("offer", attrs.getAttributeValue(i));
}
}
}
}
}
在上面的例子中,我们首先通过 getAttributeResourceValue 判断资源是否是引用类型,然后再去解析对应的资源,这样就可以打印出来了,所以说 TypedArray 可以
用来简化获取资源的流程
另外,我们说一下 declare-styleable 标签,该标签也是简化我们代码的,在自定义属性时,我们也可以省略该标签,我们改一下最上面的自定义属性声明文件:
自定义属性的声明文件(values/attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="text" format="string" />
<attr name="testAttr" format="integer" />
</resources>
自定义 View
public class MyTextView extends View {
private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
private static final int ATTR_TEXT = 0;
private static final int ATTR_TESTATTR = 1;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, mAttr);
String text = ta.getString(ATTR_TEXT, -1);
int textAttr = ta.getInteger(ATTR_TESTATTR);
ta.recycle();
}
}
我们发现,即使没有 declare-styleable 标签,我们也可以实现自定义属性,该标签只是自动帮助我们生成了 attr 数组和对应的下标序号
另外,我们在声明自定义属性时,也可以复用系统的属性:
<declare-styleable name="test">
<attr name="android:text" />
<attr name="testAttr" format="integer" />
</declare-styleable>
这里我们是使用已经定义好的属性,不需要去添加 format 属性(注意声明和使用的区别,差别就是有没有 format),然后在类中这么获取:ta.getString(R.styleable.test_android_text); 布局文件中直接 android:text="@string/hello_world" 即可。说白了,属性还是系统的属性(attr 并没有增加,只是 declared-styleable 数组中多了一个值,另外多了一个下标序号而已)