最近在学习 AIOSO(Alibaba Internal Open Source Organization,即阿里巴巴内部开源组织) 的一个子项目MMCherryUI,这是一个流式布局,可以在运行时做动态改变子元素的个数(增删查改), 并内建动画效果,先贴一张效果图出来

我们学习代码,最重要的就是动手实践。于是,我想自己去实现一个类似上面效果的页面。首先,我需要页面上的几张 icon 图标,去哪里找?上 iconfont.cn 找,里面的 icon 最全了。这时候我脑子里浮现了一个问题,我是使用 png 格式的 icon 图片还是使用 iconfont 呢?如果使用 png 我还得考虑图片的分辨率(当然,我完全可以不必考虑这个问题,毕竟我只是为了学习而去写的这个页面),但强迫症迫使我最终选择了 iconfont,因为 iconfont 可以不用考虑分辨率等适配问题。说到这里,终于进入正题了 -_- 。

iconfont 其实说白了就是一种特殊的字体,那么问题就转变为怎么在 Android 上使用自定义字体的问题了。以 iconfont 为例,一般你需要下面几个步骤:

  • 准备字体文件

    在 iconfont.cn 上选好图片,然后打包下载,解压文件,得到字体文件 iconfont.ttf。

导入字体文件

在工程 assets 目录下创建一个文件夹,名字随便取,然后把字体文件放进去。

  • 使用字体

打开工程目录下 res/values/strings.xml 文件,添加 string

  1. <string name="icons"></string>

然后打开 Activity 对应的布局文件,比如 activity_main.xml,添加 string 值到 TextView 中,

  1. TextView
  2. android:id="@+id/like"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:text="@string/icons" />

最后,为 TextView 指定字体

  1. import android.graphics.Typeface;
  2. ...
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. // 加载布局文件
  6. setContentView(R.layout.activity_main);
  7. // 加载字体文件
  8. Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
  9. // 获取 textview 实例
  10. TextView textview = (TextView)findViewById(R.id.like);
  11. // 设置 typeface, 就是字体文件
  12. textview.setTypeface(iconfont);
  13. }
  14. ...

在单个 View 的情况下,上面的步骤看起来也没有那么可怕,但是在很多 TextView、Button 等文本组件的情况下,这就是一种体力活了。

举例:

我有这么一个需求,首页底部栏需要用到四个 iconfont 的图标,


我得在 res/values/strings.xml 文件上添加四个 string (先不考虑选中状态的情况),

  1. <string name="nj_main_bottom_tab_1_icon"></string>
  2. <string name="nj_main_bottom_tab_2_icon"></string>
  3. <string name="nj_main_bottom_tab_3_icon"></string>
  4. <string name="nj_main_bottom_tab_4_icon"></string>

然后在布局文件上分别添加 string 到 TextView,最后为 TextView 指定字体

  1. Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
  2. TextView textview1 = (TextView)findViewById(R.id.icon_1);
  3. textview1.setTypeface(iconfont);
  4. TextView textview2 = (TextView)findViewById(R.id.icon_2);
  5. textview2.setTypeface(iconfont);
  6. TextView textview3 = (TextView)findViewById(R.id.icon_3);
  7. textview3.setTypeface(iconfont);
  8. TextView textview4 = (TextView)findViewById(R.id.icon_4);
  9. textview4.setTypeface(iconfont);

这只是同一个页面上有四个 iconfont 的情况,如果是多页面多处地方使用到 iconfont,那就可怕了,难以接受。身为程序员,除了会写代码,还要讲究效率和质量。我开始在想,是否有更好的方案来使用 iconfont。

由于 iconfont 属于一种字体,所以我开始从自定义字体的方向上去探索。首先想到的就是自定义 TextView 组件,这也是我最终选择的方案。但在此之前,我想跟大家分享一个国外的方案,他不考虑界面的布局层级,通过遍历当前页面上所有基于 TextView 的文本组件来设置字体。

  1. /**
  2. * Apply specified font for all text views (including nested ones) in the specified
  3. * root view.
  4. *
  5. * @param context
  6. *            Context to fetch font asset.
  7. * @param root
  8. *            Root view that should have specified font for all it's nested text
  9. *            views.
  10. * @param fontPath
  11. *            Font path related to the assets folder (e.g. "fonts/YourFontName.ttf").
  12. */
  13. public static void applyFont(final Context context, final View root, final String fontName) {
  14. try {
  15. if (root instanceof ViewGroup) {
  16. ViewGroup viewGroup = (ViewGroup) root;
  17. for (int i = 0; i < viewGroup.getChildCount(); i++)
  18. applyFont(context, viewGroup.getChildAt(i), fontName);
  19. } else if (root instanceof TextView)
  20. ((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontName));
  21. } catch (Exception e) {
  22. Log.e(TAG, String.format("Error occured when trying to apply %s font for %s view", fontName, root));
  23. e.printStackTrace();
  24. }
  25. }

使用方法:

  1. FontHelper.applyFont(context, findViewById(R.id.activity_root), "fonts/YourCustomFont.ttf");

一行代码就行了,传入上下文,根布局和字体文件路径三个参数,非常简单粗暴。刚看到这个方法时,我有些惊讶,之所以在这里拿出来跟大家分享,并不是想说这种方法有多好,而是因为我被作者的这种活跃的思维所震撼,这很值得我们去学习,不局限于传统,大胆勇于创新。

好了,不再废话了,说说我的方案——自定义 TextView。

我创建了一个 View,继承系统的 TextView,将其命名为 IconFontTextView。

然后在 res/values 目录下,新建一个 attrs.xml 的资源文件,如果已经存在,就不需要重复创建了。这是我的 attrs.xml 的内容:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="IconFontTextView">
  4. <attr name="value" format="string"/>
  5. <attr name="fontFile" format="string"/>
  6. </declare-styleable>
  7. </resources>

我定义了两个属性,value 填写 iconfont 图标对应的值,fontFile 是字体文件路径。

然后在 IconFontTextView 里添加两个构造方法

  1. public IconFontTextView(Context context) {
  2. super(context);
  3. }
  4. public IconFontTextView(Context context, AttributeSet attrs) {
  5. super(context, attrs);
  6. }

在第二个构造方法里处理属性值,具体代码如下:

  1. public IconFontTextView(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3. if (attrs == null) {
  4. return;
  5. }
  6. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IconFontTextView);
  7. final int N = a.getIndexCount();
  8. for (int i = 0; i < N; i++) {
  9. int attr = a.getIndex(i);
  10. switch (attr) {
  11. case R.styleable.IconFontTextView_value:
  12. value = a.getString(attr);
  13. setText(value);
  14. Log.d(TAG, "value : " + value);
  15. break;
  16. case R.styleable.IconFontTextView_fontFile:
  17. fontFile = a.getString(attr);
  18. Log.d(TAG, "fontFile : " + fontFile);
  19. try {
  20. Typeface typeface = Typeface.createFromAsset(context.getAssets(), fontFile);
  21. setTypeface(typeface);
  22. } catch (Throwable e) {
  23. }
  24. break;
  25. }
  26. }
  27. a.recycle();
  28. }

其实很简单,这样我们就完成了自定义 IconFontTextView 了,接下来讲下怎么使用。在布局文件中,跟普通的 TextView 一样,添加 IconFontTextVIew,

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content"
  6. android:orientation="vertical">
  7. <com.xhj.huijian.mmcherryuidemo.view.IconFontTextView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. app:fontFile="iconfont/iconfont.ttf"
  11. android:id="@+id/iconfont_view"
  12. app:value=""
  13. />
  14. <TextView
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:layout_gravity="center"
  18. android:text="搜索"/>
  19. </LinearLayout>

记得在根布局添加这一行 xmlns:app="http://schemas.android.com/apk/res-auto" ,这样才可以使用自定义属性。紧接着,指定字体文件 fontFile="iconfont/iconfont.ttf",告诉 IconFontTextView 字体文件路径位于 assets 目录下的 iconfont/iconfont.ttf,并给它一个 iconfont 的值,

  1. app:value=""
  2. <!--当然,你也可以在 res/values/strings.xml 文件上添加 string 再来引用-->
  3. app:value="@string/arrow";

这个值对应的图标是一个箭头,

这样直接跑程序就可以看到效果了,是不是方便了很多?因为一般我们不使用 TextView 来监听事件,更多的是让它去负责 View 层的一些展示,而且这里我们也不再需要在 java 代码中去指定加载字体文件,所以根本不需要再去 findViewById 获取实例了,所以可以省去下面的一段代码,

  1. Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
  2. TextView textview1 = (TextView)findViewById(R.id.icon_1);
  3. textview1.setTypeface(iconfont);
  4. TextView textview2 = (TextView)findViewById(R.id.icon_2);
  5. textview2.setTypeface(iconfont);
  6. ...

IconFontTextView 我们可以抽出来当做一个组件使用,这样以后使用 iconfont 时,将它当做普通的 TextView 来使用,并指定字体文件和图标值,就可以了。

也许你有这样的需求,我想要在运行时去动态的设置 IconFont 的值。就拿我上面那底部栏四个 tab 来说吧,当我选中其中一个 tab 时,我想要它显示选中状态,如果只是改变颜色那就方便多了,如果在改变颜色的同时,还需要改变图标呢?你可能会说,这个问题很容易解决啊,因为 IconFontTextView 是 TextView 的子类,我重新给它一个 value 不就好了吗?是的,可是当你通过 setText("&#xe6f2") 后你会发现,图标成这样了

这跟你想要的结果完全不同

这是为什么呢?我就遇到了这个问题,我还发现,当我在 res/values/strings.xml 文件上添加 string 再来使用时却不会出现这个问题,像下面这样就什么问题都没有:

  1. setText(getString(R.string.wifi));

难道 getString 里面做了什么处理吗?看了源码,没发现什么特别的地方。那好吧,我直接通过 log 打印出来,看看 getString 返回了什么。

我在 string.xml 上添加了

  1. <string name="iconfont"></string>

然后在 Activity 上添加下面两行测试代码,

  1. Log.d("tag", getString(R.string.iconfont));
  2. Log.d("tag", "");

按照以往的经验,这八九不离十跟 unicode 字符有关。把代码稍改一下

  1. setText("\ue6f2");// "&#x" 替换成 "\u",用 unicode 字符来表示

这样问题就解决了。

OK,最后附上我实现的效果图,

Android 上使用 iconfont 的一种便捷方案的更多相关文章

  1. Android上掌纹识别第一步:基于OpenCV的6种肤色分割 源码和效果图

    Android上掌纹识别第一步:基于OpenCV的6种肤色分割 源码和效果图 分类: OpenCV图像处理2013-02-21 21:35 6459人阅读 评论(8) 收藏 举报   原文链接  ht ...

  2. Android - 页面返回上一页面的三种方式

    今年刚刚跳槽到了新公司,也开始转型做Android,由此开始Android的学习历程. 最近在解很多UI的bug,在解bug过程中,总结了在UI的实现过程中,页面返回上一页面的几种实现方式. 一. 自 ...

  3. 页面结构化在 Android 上的尝试

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/M45DM5Ix7a2fmrsE8VPvxg 作者:b ...

  4. 通杀所有系统的硬件漏洞?聊一聊Drammer,Android上的RowHammer攻击

    通杀所有系统的硬件漏洞?聊一聊Drammer,Android上的RowHammer攻击 大家肯定知道前几天刚爆出来一个linux内核(Android也用的linux内核)的dirtycow漏洞.此洞可 ...

  5. Android上dip、dp、px、sp等单位说明(转)

    dip  device independent pixels(设备独立像素). 不同设备不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这个,不依赖像素. 在 ...

  6. Android上dip、dp、px、sp等单位说明

    Android上dip.dp.px.sp等单位说明 dip  device independent pixels(设备独立像素). 不同设备不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA ...

  7. [转]收集android上开源的酷炫的交互动画和视觉效果:Interactive-animation

    原文链接:http://www.open-open.com/lib/view/open1411443332703.html 描述:收集android上开源的酷炫的交互动画和视觉效果. 1.交互篇 2. ...

  8. 最牛逼android上的图表库MpChart(一) 介绍篇

    最牛逼android上的图表库MpChart一 介绍篇 MpChart优点 MpChart是什么 MpChart支持哪些图表 MpChart效果如何 最牛逼android上的图表库MpChart(一) ...

  9. Android跨进程通信的四种方式

    由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于andro ...

随机推荐

  1. The Promise of Deep Learning

    The Promise of Deep Learning By Yoshua Bengio Humans have long dreamed of creating machines that thi ...

  2. Codeforces Round #197 (Div. 2) : D

    这题也是一个线段树的水题: 不过开始题目没看明白,害得我敲了一个好复杂的程序.蛋疼啊.... 最后十几分钟的时候突然领悟到了题意,但是还是漏掉一个细节,老是过不去... 以后比赛的时候不喝啤酒了,再也 ...

  3. 李洪强iOS开发Swift篇—06_流程控制

    李洪强iOS开发Swift篇—06_流程控制 一.swift中的流程控制 Swift支持的流程结构如下: 循环结构:for.for-in.while.do-while 选择结构:if.switch 注 ...

  4. Winform的多线程问题

    http://blog.csdn.net/Maths_bai/article/details/6000744

  5. 发现一个Doxygen风格的QT帮助

    http://cep.xray.aps.anl.gov/software/qt4-x11-4.8.6-browser/classes.html http://cep.xray.aps.anl.gov/ ...

  6. C# Read/Write another Process' Memory ZZ

    Today's tutorial is about...processes' memory! In this article I'll show you how to read/write a pro ...

  7. ADB对手机进行开关机测试

    Verify issue 时,其中有条要对手机进行开关机100次,由于只有ADB环境,只能用批处理来写脚本了,代码如下: ::需配置ADB环境,开启Debug模式 ::start循环 :start s ...

  8. java基础(十四)集合(一)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

  9. How I Mathematician Wonder What You Are! - POJ 3130(求多边形的核)

    题目大意:判断多多边形是否存在内核. 代码如下: #include<iostream> #include<string.h> #include<stdio.h> # ...

  10. engine的工具中实现Snapping(捕捉)

    在Engine的工具(ITool)里: OnClick事件处理函数中: 首先需要获取一个图层,作为Snapping的参照, IFeatureLayer targetLayer 然后声明一个IMoveP ...