自定义View属性
在布局中使用自定义的View时系统会自动调用自定义View的第二个构造函数View(Context context, AttributeSet attrs),生成自定义View实例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:myColor="#ff6600"
app:myGravity="bottom"
app:mySize="200dp" />
</LinearLayout>1、自定义View属性
其中自定义的属性myColor、myGravity、mySize都是res/values/attrs.xml文件中定义的。
<declare-styleable name="MyView">
<attr name="mySize" format="dimension" />
<attr name="myColor" format="color" />
<attr name="myOrientation">
<flag name="vertical" value="1" />
<flag name="horizontal" value="0" />
</attr>
<attr name="myGravity">
<enum name="left" value="0" />
<enum name="right" value="1" />
<enum name="top" value="2" />
<enum name="bottom" value="3" />
</attr>
</declare-styleable>1.1、<declare-styleable/>自定义属性类型
在res/values文件夹下新建attrs.xml文件,支持定义各种属性,<declare-styleable/>标签表示为自定义的属性,name表示自定义View的类名。
<declare-styleable name="MyView">
...
</declare-styleable><attr/>标签中的name表示自定义的属性名。
<attr name="myGravity">
...
</attr>内部定义该属性所对应的类型,主要有一下几种数据类型:
①integer:int整型
②boolean:boolean
③color:颜色
④dimension:尺寸
⑤enum:枚举
⑥flag:位或运算,常量
⑦float:float型
⑧fraction:百分数
⑨reference:资源文件
⑩string:字符串
1.2、抽取公共属性
如果多个自定义View的属性有重复的怎么办,只需要将重复的单独提取出来,重复定义公共Attribute。在每个需要使用的地方引用即可:
<!-- 公共属性 -->
<attr name="common" format="dimension" />
<declare-styleable name="View1">
<attr name="a" format="color" />
<attr name="common" />
</declare-styleable>
<declare-styleable name="View2">
<attr name="b" format="boolean" />
<attr name="common" />
</declare-styleable>1.3、使用自定义属性
使用起来很简单,直接在布局中用app:引用即可,别忘了额外的命名空间xmlns:app="http://schemas.android.com/apk/res-auto,自动查找自定义的属性,否则找不到应用自定义的View属性。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
</LinearLayout>2、获取xml布局局中属性的值
解铃还须系铃人,自定义的View还得自己写代码去用,不然没什么用。在布局中使用自定义的View,并且设定自定义的属性,还需要在代码中借助TypedArray解析xml布局获取添加的属性:
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
int color = typedArray.getColor(R.styleable.MyView_myColor, Color.BLACK);
float length = typedArray.getDimension(R.styleable.MyView_mySize, 0.0f);
int gravity = typedArray.getInt(R.styleable.MyView_myGravity, 0);
typedArray.recycle();//使用完要回收
... ...
}2.1、获取自定义属性值
之前已经介绍过了,自定义的属性在res/values/attrs.xml文件中,可以通过R.资源获取到:
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
//获取自定义的属性myColor,给个默认值
int color = typedArray.getColor(R.styleable.MyView_myColor, Color.BLACK);2.2、获取android预定义属性值
当然除了自定义的,我们更像获取到在xml布局文件中设置的各种属性,诸如:layout_height、layout_width等:
①定义一个int[]数组,里面是我们想要获取到的资源id。必须按照升序排列,否则将不能正确获取到对应属性的值。
//属性资源id数组
int[] array = new int[]{android.R.attr.layout_width, android.R.attr.layout_height}②传入int[]数组。
//和第一种一样,传入int[]数组。
TypedArray typedArray = context.obtainStyledAttributes(attrs, array);前面用到的自定义属性R.styleable.MyView其实也是一个int[]类型的数组
int[] arrays = R.styleable.MyView;③按照int[]数组中对应的索引读取数据
//数组中第0个索引,对应的属性就是android.R.attr.layout_width
float width = typedArray.getDimension(0, 0.0f);
float height = typedArray.getDimension(1, 0.0f);④别忘了回收资源
typedArray.recycle();2.3、坑点
前面说过,获取属性是传入的int[]数组中的元素需要按照降序排列,否则无法获取到对应属性的值。
int[] array = new int[]{android.R.attr.layout_width, android.R.attr.layout_height}这个让我足足折腾了一下午时间,自己脑洞大开,又是bug终结者!想能不能把int[]数组中的顺序颠倒一下再获取,结果获取到的值发生错乱,有些属性则获取不到值。
int[] array = new int[]{android.R.attr.layout_height,android.R.attr.layout_width}查看官方文档说明才发现obtainStyledAttributes (int resId,int[] attrs),属性数组中id的顺序一定要升序才可以!!!天坑啊。
Return a TypedArray holding the values defined by the style resource resid which are listed in attrs.
Be sure to call TypedArray#recycle() when you are done with the array.
| Parameters | |
|---|---|
resId | int: The desired style resource. |
attrs | int: The desired attributes in the style. These attribute IDs must be sorted in ascending order. This value must never be null. |
3、TextView属性源码阅读
Android已有View的示例TextView,是如何定义和使用属性的:
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}参考TextView的构造方法,defStyleAttr入参使用了R.attr.textViewStyle。在系统源码的attrs.xml中找到textViewStyle的定义:
... ...
<!-- Default TabWidget style. -->
<attr name="tabWidgetStyle" format="reference" />
<!-- Default TextView style. -->
<attr name="textViewStyle" format="reference" />
<!-- Default WebTextView style. -->
<attr name="webTextViewStyle" format="reference" />
... ...不难继续跟踪,在系统的values.xml中定义了对应的资源:
<item name="android:textViewStyle">@style/Widget.AppCompat.TextView</item>而上面这个就是TextView默认的theme主题:
<style name="Widget.AppCompat.TextView" parent="Base.Widget.AppCompat.TextView"/>经过层层向上追踪到TextView系统默认主题的完整定义:
<style name="Widget.TextView">
<item name="textAppearance">?attr/textAppearanceSmall</item>
<item name="textSelectHandleLeft">?attr/textSelectHandleLeft</item>
<item name="textSelectHandleRight">?attr/textSelectHandleRight</item>
<item name="textSelectHandle">?attr/textSelectHandle</item>
<item name="textEditPasteWindowLayout">?attr/textEditPasteWindowLayout</item>
<item name="textEditNoPasteWindowLayout">?attr/textEditNoPasteWindowLayout</item>
<item name="textEditSidePasteWindowLayout">?attr/textEditSidePasteWindowLayout</item>
<item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item>
<item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
<item name="textEditSuggestionContainerLayout">?attr/textEditSuggestionContainerLayout</item>
<item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
<item name="textCursorDrawable">?attr/textCursorDrawable</item>
<item name="breakStrategy">high_quality</item>
<item name="hyphenationFrequency">@dimen/config_preferredHyphenationFrequency</item>
</style>通过类似TextView的方法,Android系统为很多原生控件提供了默认属性值。相信大家也可以理解,defStyleAttr参数是当前theme下定义的一个style资源的属性,并为最后生成的TypedArray资源数组提供了默认值的功能。
相关资料:

