内容简介:对于自定义属性,遵循以下几步,就可以实现:那么,我有几个问题,如果回答的很好,下面的文章就不用看了,可以跳过:构造方法中的有个参数叫做
对于自定义属性,遵循以下几步,就可以实现:
- 自定义一个
CustomView
(extends View 或者 ViewGroup )类 - 编写
values/attrs.xml
,在其中编写styleable
和attr
等标签元素 - 在布局文件中
CustomView
使用自定义的属性 - 在CustomView的构造方法中通过TypedArray获取
那么,我有几个问题,如果回答的很好,下面的文章就不用看了,可以跳过:
- 以上步骤是如何奏效的?
- styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?
- 如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
- 构造方法中的有个参数叫做
AttributeSet
(eg:CustomView(Context context, AttributeSet attrs)
)这个参数看名字就知道包含的是参数的数组,那么我能不能通过它去获取我的自定义属性呢? -
TypedArray
是什么鬼?从哪冒出来的,就要我去使用?
自定义属性使用示例
- 自定义属性的声明文件如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomViewTest"> <attr name="testText" format="string"/> <attr name="testInteger" format="integer"/> </declare-styleable> </resources> 复制代码
- 自定义CustomView
public class CustomView extends View { ··· public CustomView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomViewTest); String testText = a.getString(R.styleable.CustomViewTest_testText); int testInteger = a.getInteger(R.styleable.CustomViewTest_testInteger, 10); Log.e(TAG, "testText =" + testText + " ,testInteger=" + testInteger); a.recycle(); } ··· } 复制代码
- 布局文件使用
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout ··· <!-- 自动查找属性 --> xmlns:app="http://schemas.android.com/apk/res-auto" "> <com.zeroxuan.customviewtest.CustomView android:layout_width="match_parent" android:layout_height="match_parent" app:testInteger="10086" app:testText="zeroXuan" /> </android.support.constraint.ConstraintLayout> 复制代码
- 运行结果如下:
注意:我的styleable的name写的是CustomViewTest,所以说这里并不要求一定是自定义View的名字。
AttributeSet 与 TypedArray
构造方法中的有个参数叫做 AttributeSet
(eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?
首先 AttributeSet
中的确保存的是该 View
声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢?如下:
public CustomView(Context context, AttributeSet attrs) { super(context, attrs); final int count = attrs.getAttributeCount(); for (int i = 0; i < count; i++) { String attrName = attrs.getAttributeName(i); String attrValue = attrs.getAttributeValue(i); Log.e(TAG, "attrName= " + attrName + " ,attrValue=" + attrValue); } } 复制代码
输出:
咦,真的可以获得所有的属性。通过 AttributeSet
可以获得布局文件中定义的所有属性的key和value,那么是不是说 TypedArray
就可以抛弃了呢?答案是: NO,NO,No
,重要的事,说三遍!。
TypedArray是什么?
- 现在简单修改一下布局文件为:
<com.zeroxuan.customviewtest.CustomView android:layout_width="match_parent" android:layout_height="match_parent" app:testInteger="10086" app:testText="@string/my_name" /> 复制代码
- 解析过程
public CustomView(Context context, AttributeSet attrs) { super(context, attrs); final int count = attrs.getAttributeCount(); for (int i = 0; i < count; i++) { String attrName = attrs.getAttributeName(i); String attrValue = attrs.getAttributeValue(i); Log.e(TAG, "attrName= " + attrName + " ,attrValue=" + attrValue); } Log.e(TAG, ">> Use TypedArray"); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomViewTest); String testText = a.getString(R.styleable.CustomViewTest_testText); int testInteger = a.getInteger(R.styleable.CustomViewTest_testInteger, 10); Log.e(TAG, "testText =" + testText + " ,testInteger=" + testInteger); a.recycle(); } 复制代码
- 运行结果
通过运行结果可以看出,使用 AttributeSet
获取的值,如果是引用都变成了 @+数字
的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了。
TypedArray
其实就是用来简化我们解析自定义属性工作的 。比如上例,如果布局中的属性的值是引用类型,如果使用AttributeSet去获得最终的 testText
取值,那么需要第一步拿到id,第二步再去解析id。 而TypedArray正是帮我们简化了这个过程
。
如果通过AttributeSet获取最终的 testText
取值的过程如下:
//使用索引 3 ,是因为testText在CustomView中的索引是3 int resId=attrs.getAttributeResourceValue(3,-1); Log.e(TAG, "attrName= "+getResources().getString(resId) ); 复制代码
ok,现在别人问你TypedArray存在的意义,你就可以告诉他, TypedArray
其实就是用来简化解析自定义属性工作流程的 。
attr 和 declare-styleable的关系
首先要明确一点, attr
不依赖于 declare-styleable
, declare-styleable
只是为了方便 attr
的使用。
我们自己定义的属性完全可以不放到 declare-styleable
里面,比如直接在resources文件中定义一些属性:
<attr name="custom_attr1" format="string" /> <attr name="custom_attr2" format="string" /> 复制代码
定义一个 attr
就会在R文件里面生成一个 attr
类型的资源 Id
,那么我们去获取这个属性时,必须调用如下代码:
int[] custom_attrs = {R.attr.custom_attr1,R.custom_attr2}; TypedArray typedArray = context.obtainStyledAttributes(set,custom_attrs); 复制代码
而通过定义一个 declare-styleable
,我们可以在R文件里自动生成一个 int[]
,数组里面的 int
就是定义在 declare-styleable
里面的 attr的id
。所以我们在获取属性的时候就可以直接使用 declare-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 name="test"> <!-- 使用系统属性或者已经定义好的属性,不需要去添加format属性 --> <attr name="android:text" /> <attr name="testAttr" format="integer" /> </declare-styleable> 复制代码
然后在类中这么获取: a.getString(R.styleable.CustomViewTest_android_text);
布局文件中直接 android:text="zeroXuan is my name"
即可。
obtainStyledAttributes的详细说明
- obtainStyledAttributes(int[] attrs):从当前系统主题中获取 attrs 中的属性,最终调用是
public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0); } 复制代码
- obtainStyledAttributes(int resid, int[] attrs):从资源文件中获取 attrs 中的属性。
public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs) throws NotFoundException { return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId); } 复制代码
- obtainStyledAttributes(AttributeSet set, int[] attrs):从 layout 设置的属性中获取 attrs 中的属性。
public final TypedArray obtainStyledAttributes( AttributeSet set, @StyleableRes int[] attrs) { return getTheme().obtainStyledAttributes(set, attrs, 0, 0); } 复制代码
- obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes):下面细说。
public final TypedArray obtainStyledAttributes( AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { return getTheme().obtainStyledAttributes( set, attrs, defStyleAttr, defStyleRes); } 复制代码
可以看出最终都是调用方法4,现在主要分析方法 obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
,其中这个方法的四个参数解释如下:
- AttributeSet set: 一个和xml中的标签关联的存放属性的集合.
- int[] attrs: 我们要在xml中读取的属性.
- int defStyleAttr: 这是当前Theme中的包含的
一个指向style的引用
.当我们没有给自定义View设置declare-styleable资源集合时,默认从这个集合里面查找布局文件中配置属性值.传入0表示不向该defStyleAttr中查找默认值. - int defStyleRes: 这个也是
一个指向Style的资源ID
,但是仅在defStyleAttr为0或者defStyleAttr不为0但Theme中没有为defStyleAttr属性赋值时起作用.
这么说可能有点迷糊,来一个例子希望你能立马领悟!
- 首先自定义属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomView"> <attr name="testText1" format="string"/> <attr name="testText2" format="string"/> <attr name="testText3" format="string"/> <attr name="testText4" format="string"/> <attr name="testText5" format="string"/> <attr name="attr_defStyle" format="reference"/> </declare-styleable> </resources> 复制代码
其中attr_defStyle
属性名,就是obtainStyledAttributes
中的第三个参数。 - 定义
Style
和Theme
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="attr_defStyle">@style/style_attr_defStyleAttr</item> <item name="testText1">testText1-declare in Theme</item> <item name="testText2">testText2-declare in Theme</item> <item name="testText3">testText3-declare in Theme</item> <item name="testText4">testText4-declare in Theme</item> <item name="testText5">testText5-declare in Theme</item> </style> <!-- 用来表示 defStyleRes--> <style name="style_defStyleRes"> <item name="testText1">testText1-declare in style_defStyleRes</item> <item name="testText2">testText2-declare in style_defStyleRes</item> <item name="testText3">testText3-declare in style_defStyleRes</item> <item name="testText4">testText4-declare in style_defStyleRes</item> </style> <!-- 用来表示 attr_defStyleAttr 这个属性的值 --> <style name="style_attr_defStyleAttr"> <item name="testText1">testText1-declare in style_attr_defStyleAttr</item> <item name="testText2">testText2-declare in style_attr_defStyleAttr</item> <item name="testText3">testText3-declare in style_attr_defStyleAttr</item> </style> <!-- 直接在布局中的 style 中使用 --> <style name="style_CustomViewStyle"> <item name="testText1">testText1-declare in style_CustomViewStyle</item> <item name="testText2">testText2-declare in style_CustomViewStyle</item> </style> </resources> 复制代码
- 自定义View
public class CustomView extends View { private static final String TAG = "CustomView"; public CustomView(Context context) { this(context, null); } public CustomView(Context context, AttributeSet attrs) { //R.attr.attr_defStyle 就是defStyleRes this(context, attrs, R.attr.attr_defStyle); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, R.style.style_defStyleRes); } @TargetApi(21) public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TypedArray a = context .obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, defStyleRes); String text1 = a.getString(R.styleable.CustomView_testText1); String text2 = a.getString(R.styleable.CustomView_testText2); String text3 = a.getString(R.styleable.CustomView_testText3); String text4 = a.getString(R.styleable.CustomView_testText4); String text5 = a.getString(R.styleable.CustomView_testText5); Log.e(TAG, "text1== " + text1); Log.e(TAG, "text2== " + text2); Log.e(TAG, "text3== " + text3); Log.e(TAG, "text4== " + text4); Log.e(TAG, "text5== " + text5); a.recycle(); } } 复制代码
- 布局界面
<com.zeroxuan.customviewtest.CustomView style="@style/style_CustomViewStyle" android:layout_width="match_parent" android:layout_height="match_parent" app:testText1="Direct declare in XML" /> 复制代码
- 运行结果
调用顺序
优先 次之 其次 最后使
注意 defStyleAttr的值一定要在Theme中设置才有效果,就拿上面的例子说,如果你没有在Theme中给R.attr.attr_defStyle赋值,而是直接在布局文件中赋值,这样做是没有效果的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Android 自定义 View (04自定义属性)
- 浅谈Spring Boot 属性配置和自定义属性配置
- 初识css自定义属性
- [译] ECMAScript 类 —— 定义私有属性
- CSS变量(自定义属性)实用指南
- 使用CSS自定义属性构建骨架屏
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
R for Data Science
Hadley Wickham、Garrett Grolemund / O'Reilly Media / 2016-12-25 / USD 39.99
http://r4ds.had.co.nz/一起来看看 《R for Data Science》 这本书的介绍吧!