理解Android中的自定义属性
更新日期:2016年1月29日,具体时间为上午10点52分42秒;文章的作者是鸿洋。
本文着重阐述了对Android平台自定义属性的理解,指出在Android应用开发中,某些既定属性往往无法完全满足开发者的需求。因此,有必要对控件及其属性进行自定义。针对这一议题,文章将进行深入探讨,供有需要者参阅。
本文实例讲解了Android中的自定义属性,具体内容如下
1、引言
关于自定义属性,大家应该都很熟悉,只需按照以下步骤进行,即可轻松实现:
ps:如果你对上述几个步骤不熟悉,建议先熟悉下,再继续~
那么,我有几个问题:
嗯开yun体育app官网网页登录入口,对于这些问题,大家不妨思考一下,该如何作答?或者,掌握这四个步骤,背诵下来就足够了~~
2、常见的例子
接下来,我们通过具体的实例来解答这个问题,而解答的顺序则不固定。首先,请大家先观察一个典型的例子,那就是上述步骤的代码实现。
自定义属性的声明文件
自定义View类
package com.example.test;
引入android应用程序的上下文环境。
引入android内容资源类型数组。
引入android系统中的util包下的AttributeSet类。
import android.util.Log;
import android.view.View;
public class MyTextView 拓展了 View 类,构成了一个继承自 View 的自定义文本视图。
定义了一个名为TAG的私有静态常量字符串,其值为MyTextView类通过getSimpleName()方法获取的简单类名。
public MyTextView 被创建于特定的上下文环境,并接收了属性集参数,以供后续使用。
super(context, attrs);
通过调用context的obtainStyledAttributes方法,获取了attrs对象中定义的test类型的属性,并将结果赋值给TypedArray类型的变量ta。
获取资源文件中的字符串值,通过调用`ta`对象的方法,并指定`R.styleable.test_testAttr`作为属性标识符。
获取text属性的整数值,若在test_text资源文件中未指定,则默认为-1。
输出日志信息,显示文本内容为:“text = ”后跟text变量的值,逗号分隔,接着是“textAttr = ”后跟textAttr变量的值。
ta.recycle();
}
}
布局文件中使用
ok,大家花3s扫一下,运行结果为:
MyTextView的text属性值为helloworld,而textAttr属性的值为520。
大家应该都清楚这一点吧,留意一下,我设置的styleable的属性名为test,因此这里并不强制要求必须与自定义视图的名称相一致。
3、AttributeSet与TypedArray
下面考虑:
在构造函数中,存在一个名为AttributeSet的参数(例如:MyTextView(Context context, AttributeSet attrs))kaiyun.ccm,从其名称可以推断出它代表的是一系列参数的集合。那么,我是否能够利用这个参数来获取我自定义的属性呢?
AttributeSet中保存了View所声明的全部属性,而且确实可以通过它来获取(包括自定义的)属性,那么具体操作方法又是怎样的呢?
实际上,查阅一下AttributeSet的相关方法即可领悟,接下来,请查看代码部分。
public MyTextView 被创建于特定的上下文环境,并接收了属性集参数,以供后续使用。
super(context, attrs);
获取属性的数量后,变量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);
}
// ==>use typedarray ...
}
输出:
MyTextView(4136)中,属性名为layout_width,其值设定为100.0dip。 MyTextView(4136)中显示,属性名为layout_height,其值设定为200.0dip。 MyTextView(4136)显示:属性名为text,属性值为helloworld。 MyTextView(4136)中显示,属性名称为testAttr,对应的属性值为520。
结合上面的布局文件,你发现了什么?
哎呀,果不其然,效果真的很神奇,竟然真的获得了全部属性。嗯,没错,我们可以通过AttributeSet获取到布局文件中定义的所有属性的键和值(还有其他一些方法,大家可以自行探索)kaiyun全站网页版登录,那这么说来,我们是不是就可以把那个麻烦的TypedArray给扔掉了呢?答案却是:不行!
现在关注下一个问题:
TypedArray是什么鬼?从哪冒出来的,就要我去使用?
我们简单修改下,布局文件中的MyTextView的属性。
现在再次运行的结果是:
MyTextView(4692)中显示,属性名为layout_width,对应的属性值为@2131165234。 MyTextView(4692)显示:属性名为layout_height,属性值为@2131165235。 MyTextView(4692)显示:属性名称为text,属性值为@2131361809。 MyTextView(4692)中,属性名称为testAttr,其属性值为520。 >>use typedarray MyTextView(4692)中显示的文本内容为“Hello world!”,其文本属性值为520。
发现了什么情况?通过AttributeSet获取到的数值,若为引用,便转化成了形如@+数字的字符串表示。你能否理解这其中的含义?再来看最后一行,通过TypedArray获取的值,相信你立刻就能领悟其中的奥秘了。
TypedArray实际上是为了减轻我们的工作负担而设计的,以之前的例子为例,若布局中属性的数值属于引用类型,如@dimen/dp100,若要通过AttributeSet获取最终的像素值,则必须先获取id,然后才能对id进行解析。正是TypedArray简化了这一复杂的过程。
若要获取最终像素值,需按照以下步骤进行:通过AttributeSet进行操作,然后进行一系列处理。
获取宽度尺寸资源ID的操作是通过调用attrs对象的getAttributeResourceValue方法完成的,参数分别为0和-1,最终结果被赋值给变量widthDimensionId。 在日志输出中记录,"布局宽度设置为:",紧跟着是获取资源维度值的结果,即:"layout_width=" + getResources().getDimension(widthDimensionId)"。
好的,若他人询问关于TypedArray的重要性,你便能够向他们阐述。
4、declare-styleable
我们已成功处理了两个问题,现在转向查看布局文件,其中存在一个名为“zhy:text”的属性。
众所周知,系统内置了一个属性,名为“android:text”。个人认为,直接采用“android:text”会更加简便。如此一来,我们便可以思考以下问题:
如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
答案是可以的,怎么做呢?
在attrs.xml文件中,应直接应用android:text属性。
在此情况下,我们应当留意,我们采用的是既已设定的属性,无需额外添加所谓的format属性。需要注意的是,这里的声明与使用存在差异,关键在于是否包含format这一元素。
在类中,我们可以通过以下方式获取:使用ta.getString方法,传入R.styleable.test_android_text作为参数;而在布局文件中,只需直接将android:text属性设置为@string/hello_world即可。
这里需要说明的是,系统内设定的属性,其设置方法与我们自行设定的属性相仿,您可以在sdk/platforms/android-xx/data/res/values这一路径下观察到系统所定义的属性。接着,你可以在系统所提供的View(例如TextView)的创建方法中找到用于获取属性信息的TypedArray相关代码(请自行查阅)。
好的,那么接下来,我在思考,既然“declare-styleable”标签的名称可以任意设定,如此自由,那么我们不妨思考一下:
styleable这个词究竟有何意?难道真的可以省略不提吗?既然我已经自行定义了属性,为何还需声明styleable这一特性?
其实的确是可以不写的,怎么做呢?
首先删除declare-styleable的标签
那么现在的attrs.xml为:
* MyTextView实现
package com.example.test;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class MyTextView extends View {
private static final String TAG = MyTextView.class.getSimpleName();
私有常量数组mAttr包含了以下两个属性引用:来自Android系统的text属性引用,以及自定义的testAttr属性引用。
私有静态最终常量,属性名为Android文本,其值为零。
私有常量ATTR_TESTATTR被定义为1。
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// ==>use typedarray
通过调用context的obtainStyledAttributes方法,获取了包含attrs参数的mAttr属性的TypedArray对象。
获取字符串值,通过调用ta对象的方法,指定属性名为ATTR_ANDROID_TEXT。
获取的文本属性值为ta对象中ATTR_TESTATTR键对应的整数值,若该键不存在则默认为-1。
输出文本内容为“Hello world!”,其属性值为520。
Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
ta.recycle();
}
}
似乎代码有所增加,从中我们可以观察到声明了一个整型数组,该数组中的各个元素即为所要获取的attr的ID。接着,我们依据这些元素在数组中的具体位置,设定了一些整型常量来标识它们的索引,并通过TypedArray来提取这些值。
可以看到,我们原本的:
R.styleable.test => mAttr R.styleable.test_text 对应于 ATTR_ANDROID_TEXT(0)。 R.styleable中的test_testAttr对应于ATTR_TESTATTR编号为1。
那么实际上,Android系统内部同样如此操作,依照常规的编写方式,它会在R.java文件中生成以下代码:
public static final 类 attr {
定义了一个常量,其值为十六进制的0x7f0100a9,该常量代表测试属性。
}
public static final 类的名称为 styleable{
定义了一个名为test_android_text的常量整型变量,其值为零。
public static final int 测试属性标识符 = 1;
public static final int[] test = {}; 这一组数据是固定的,不进行任何修改。
0x0101014f, 0x7f0100a9
};
}
好的,通过上述内容,你应当察觉到系统中的styleale功能。这一功能能够帮助我们自动生成众多常规内容,例如整数数组、索引常量等,从而极大地简化了我们的开发流程(试想,若需手动编写大量属性常量,代码将变得多么复杂)。那么,大家想必对declare-styleable的name属性有所了解,通常情况下,我们会在其中写入自定义View的类名。这一做法主要是为了便于直观理解,declare-styleable的这些属性,实际上都是针对该View进行设置的。
实际上,掌握这一原理颇为有益,具体可参考:在Android平台上,如何优雅地创建自定义控件以实现元素间的分隔线。
ok,现在5个问题,回答了4个,第一个问题:
自定义属性的几个步骤是如何奏效的?
是的,前面的内容已经大致包括了问题的解答,大家自行归纳总结,因此:无需详述。
总结:
这就是关于Android平台自定义属性的全部介绍,期待它能对大家的学术研究带来一定的帮助。

