一、attr和styleable
自定义View,如果想在xml指定参数(例如:改变字体颜色,字体大小),需要声明一个styleable,并在里面自己定义一些attr属性。
attr不依赖于styleable,styleable只是为了方便attr的使用。
不使用styleable
自定义属性完全可以不放到styleable里面,比如直接在resources文件中定义一些属性:
<attr name="custom_attr1" format="string" />
<attr name="custom_attr2" format="string" />
一个attr对应R文件的一个id,使用下面的方式获取属性
int[] custom_attrs = {R.attr.custom_attr1,R.custom_attr2};
TypedArray typedArray = context.obtainStyledAttributes(set,custom_attrs);
使用styleable
定义一个styleable,在R文件里会自动生成一个int[],数组里面的int就是定义在styleable里面attr的id。所以我们在获取属性的时候就可以直接使用styleable数组来获取一系列的属性。
<declare-styleable name="custom_attrs">
<attr name="custom_attr1" format="string" />
<attr name="custom_attr2" format="string" />
</declare-styleable>
获取:
TypedArray typedArray = context.obtainStyledAttributes(set,R.styleable.custom_attrs);
定义一个declare-styleable,自动提供了一个属性数组,使用这个数组获取属性。此外,我觉得使用declare-styleable的方式有利于把相关的属性组织起来,有一个分组的概念,属性的使用范围更加明确。
二、改变属性值
属性值的改变,有下面五种方式:
1、在xml中设置属性值
2、在xml中设置style
3、在Theme中设置style
4、在Theme中设置属性值
5、自定义view中设置默认style
三、obtainStyledAttributes()获取属性
既然上面设置属性的值,有5个地方,那么获取属性的值,也会有5个地方(见下面蓝色字样),且分优先级。
在前面已经使用了obtainStyledAttributes来获取属性了,现在来看看这个函数的声明吧:
//从xml中获取attrs中属性的值
obtainAttributes(AttributeSet set, int[] attrs);
//从Theme中获取attrs中属性的值
obtainStyledAttributes(int[] attrs);
//从资源文件定义的style中读取属性
obtainStyledAttributes(int resId,int[] attrs);
//多个数据源
obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes);
获取属性,参数主要分为两类:第一,我需要获取哪些属性;第二:我从哪里去获取这些属性(数据源)。
attrs
attrs:int[],每个方法中都有的参数,就是告诉系统需要获取哪些属性。
set
set:表示从xml文件为这个View添加的属性。注意,由LayoutInflater加载进来的布局或者View才有这个属性集。
两种数据源:
- 使用android:layout_width=”wrap_content”直接指定,1、在xml中设置属性值
- 通过style=”@style/somestyle”指定的。2、在xml中设置style
defStyleAttr
3、在Theme中设置style
在系统Theme设置style ,修改所有的样式,一般会这么做:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="custom_style">@style/custom_theme</item>
</style>
<style name="custom_theme">
<item name="custom_color1">#36a39b</item>
<item name="custom_color2">#36a39b</item>
<item name="custom_color3">#36a39b</item>
</style>
<attr name="custom_style" format="reference"></attr>
上面代码中的custom_style 是style 名称,custom_theme是style 的内容
public CustomView(Context context, AttributeSet attrs) {
//defStyleAttr参数的值为 R.attr.custom_style
this(context, attrs, R.attr.custom_style);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//调用了obtainStyledAttributes的四参函数
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.custom_attrs, defStyleAttr, R.style.default_style);
}
resId=defStyleRes
5、自定义view中设置默认style
resId or defStyleRes:直接从资源文件定义的style中获取属性值。
<style name="default_style">
<item name="custom_color1">#909304</item>
<item name="custom_color2">#909304</item>
<item name="custom_color3">#909304</item>
<item name="custom_color4">#909304</item>
<item name="custom_color5">#909304</item>
</style>
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//调用了obtainStyledAttributes的四参函数
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.custom_attrs, defStyleAttr, R.style.default_style);
}
obtainStyledAttributes(int[] attrs);
这个函数并未指定数据源,他是在Theme中获取指定属性的值。4、在Theme中设置属性值
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="custom_color1">#32b430</item>
<item name="custom_color2">#32b430</item>
<item name="custom_color3">#32b430</item>
<item name="custom_color4">#32b430</item>
</style>
TypedArray
我们看到在获取到属性值之后,都会返回一个TypedArray对象,它又是什么鬼?
TypedArray主要有两个作用,
- 第一是内部转换attrid和属性值数组的关系;
- 第二是提供了一些类型的自动转化,比如我们getString时,TypedArray会自动将ResId对应的string从资源文件中读出来。
四、优先级
优先级从高到低:
- 在xml中设置属性值
- 在xml中设置style
- 在Theme中设置style
- 不为0, 则不使用 自定义view中设置的默认style
- 为0,使用自定义view中设置的默认style
- 在Theme中设置属性值
五、代码
https://github.com/JantHsueh/CustomAttrExmp
参考:深入理解Android 自定义attr Style styleable以及其应用
关注我的公众号,轻松了解和学习更多技术