[勘误]我在我的上篇博客一种可以代替图标的字符集Fontawesome一文中写到Fontawesome字体集在AS中是无法在布局编辑器中直接查看显示效果,这一结论是错误的,今天工作中需求要求有一个字体图标会出现多次,如果采用博客中提到的方式处理,会重复定义许多id,代码重复,因此寻思通过自定义属性为TextView设置typeface,这样可以直接在布局中就可以实现使用fontawesome,岂不快哉!
看需求,是要在所有的条目右边加上一个“右箭头”的字体图标,很显然这些字体图标对应同一个unicode值,沿用之前的方法会造成大量重复代码,所以我想如果可以直接在布局上设置TextView的typeface不是很便捷嘛,撸起袖子加油干~
一、为TextView自定义属性 mytypeface
自定义属性在平时的开发中,还是属于比较常见的需求,这个项目中我是想为TextView设置一个自定义属性mytypeface来在布局中直接指定typeface,从而避免在代码中一个个控件的findViewById设置。刚好复习下自定义控件的具体实现:
1. 自定义属性定义在什么地方
通常来说,自定义属性声明和定义在values/attrs.xml中,如果没有请创建即可,在attrs.xml中有2个重要的节点:declare-styleable/attr ,其中declare-styleable意思是指宣布/定义的意思,需要指出的是其name属性值就是attr属性的集合名称可以随便起,不一定要是我们的自定义控件的名称,而接下来的节点attr自然就是具体的属性名称,如mytypeface.
- 我的自定义属性的声明文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyIconTextView">
<attr name="mytypeface" format="string"/>
</declare-styleable>
</resources>
这里,要提一句,attr节点的format属性不是必须的,我这里为什么要定义mytypeface是string类型?这个就要看TextView的源码啦,因为我是想通过在布局中为TextView设置属性自动完成让TextView使用Fontawesome字体,那么我就必须要知道TextView是如何为其设置typeface的。
我们知道:系统的控件属性都是定义在路径sdk\platforms\android-23\data\res\values\attrs.xml中的
可以看见,在系统属性中,使用enum枚举类型将typeface定义为四个值,分别是normal/sans/serif/monospace。追踪TextView的源码发现如下蛛丝马迹:
看最终调用处,是不是可以把在xml设置的typeface的enum值取出来并进行分支判断从而应用,因此我想其实可以直接自定义属性来避免重复在代码中为TextView设置使用Fontawesome字体。看到这里,你应该知道我为什么要把typeface定义成 string类型,还得猜到我要设置什么样的字符串?纳尼,还没看懂~好吧继续向下看,吼~吼~
2. 自定义属性什么时候使用到(AttributeSet与TypedArray的区别)
在上面其实,我已经分析了,我们在xml中定义的属性是何时被解析出来并使用的,那就是在TextView的构造中:
最终调用的是参数最多的构造,在构造的参数中有1个参数需要重点解释下:
- 参数:AttributeSet:
顾名思义就是指所有被系统或自定义的属性的set集合(当然,实际包含的内容以我们在布局中使用到相关属性),很显然这里面有我们关心的属性,这里是取出属性name及其value的一个方法,但是缺点是如果xml的属性的value不是值而是一个资源引用id,那么取出具体值势必需要进一步解析id,比较麻烦,因此有了TypedArray。
更细节的内容,建议参考Android 深入理解Android中的自定义属性
3.自定义属性使用需要注意什么问题
- 使用自定义属性需要注意的是使用namespace,即命名空间,一般可以xmlns:你的命名空间名称,如我的就是xmlns:wx=”http://schemas.android.com/apk/res-auto”
二、自定义MyIconTextView
已经确定好自定义属性,要想使用自定义属性,就必须进行自定义控件,在构造中获取自定义属性并应用到控件上,因为本文基于TextView控件,因此自定义MyIconTextView extends TextView,代码如下:
public class MyIconTextView extends TextView {
public MyIconTextView(Context context) {
this(context, null);
}
public MyIconTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyIconTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyIconTextView);
String typeface = typedArray.getString(R.styleable.MyIconTextView_mytypeface);
setTypeface(FontManager.getTypeface(context, typeface), 0);//使用FontManager核心类
typedArray.recycle(); //注意回收
}
@Override
public void setTypeface(Typeface tf, int style) {
super.setTypeface(tf, style);
}
}
这里需要指出的是,如何访问到我们定义在attrs.xml中的自定义属性呢?答案是,通过类TypedArray 实现的,R.styleable.MyIconTextView中所有的attr在R.java中都有映射,要想访问到declare-styleable的子标签attr必须通过R.java实现,因为所有的attr在R.java中以declare-styleable’s name_attr’s name生成了对应的常量,aapt干的。
三、在布局xml中MyIconTextView应用自定义属性mytypeface
以上工作都做完后,就可以自由在布局xml中使用自定义的MyIconTextView并直接引用属性mytypeface,看看下面的布局文件,博主没想到这么做后,布局编辑器竟然可以实时看出字体绘制后显示的图标效果,搜嘎,so good……
<com.zbiti.yuntu.view.MyIconTextView
android:layout_width="18dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center"
android:text="@string/fa_angle_right"
android:textSize="16sp"
wx:mytypeface="fonts/fontawesome-webfont.ttf" />
布局编辑器看来,在写xml时,就已经调用了构造对控件进行了渲染显示啦
oh~