[勘误]我在我的上篇博客一种可以代替图标的字符集Fontawesome一文中写到Fontawesome字体集在AS中是无法在布局编辑器中直接查看显示效果,这一结论是错误的,今天工作中需求要求有一个字体图标会出现多次,如果采用博客中提到的方式处理,会重复定义许多id,代码重复,因此寻思通过自定义属性为TextView设置typeface,这样可以直接在布局中就可以实现使用fontawesome,岂不快哉!
看需求,是要在所有的条目右边加上一个“右箭头”的字体图标,很显然这些字体图标对应同一个unicode值,沿用之前的方法会造成大量重复代码,所以我想如果可以直接在布局上设置TextView的typeface不是很便捷嘛,撸起袖子加油干~自定义属性在平时的开发中,还是属于比较常见的需求,这个项目中我是想为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”已经确定好自定义属性,要想使用自定义属性,就必须进行自定义控件,在构造中获取自定义属性并应用到控件上,因为本文基于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,看看下面的布局文件,博主没想到这么做后,布局编辑器竟然可以实时看出字体绘制后显示的图标效果,搜嘎,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~