Android 上使用 iconfont 的一种便捷方案
最近在学习 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
- <string name="icons"></string>
然后打开 Activity 对应的布局文件,比如 activity_main.xml,添加 string 值到 TextView 中,
- TextView
- android:id="@+id/like"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/icons" />
最后,为 TextView 指定字体
- import android.graphics.Typeface;
- ...
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 加载布局文件
- setContentView(R.layout.activity_main);
- // 加载字体文件
- Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
- // 获取 textview 实例
- TextView textview = (TextView)findViewById(R.id.like);
- // 设置 typeface, 就是字体文件
- textview.setTypeface(iconfont);
- }
- ...
在单个 View 的情况下,上面的步骤看起来也没有那么可怕,但是在很多 TextView、Button 等文本组件的情况下,这就是一种体力活了。
举例:
我有这么一个需求,首页底部栏需要用到四个 iconfont 的图标,
我得在 res/values/strings.xml 文件上添加四个 string (先不考虑选中状态的情况),
- <string name="nj_main_bottom_tab_1_icon"></string>
- <string name="nj_main_bottom_tab_2_icon"></string>
- <string name="nj_main_bottom_tab_3_icon"></string>
- <string name="nj_main_bottom_tab_4_icon"></string>
然后在布局文件上分别添加 string 到 TextView,最后为 TextView 指定字体
- Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
- TextView textview1 = (TextView)findViewById(R.id.icon_1);
- textview1.setTypeface(iconfont);
- TextView textview2 = (TextView)findViewById(R.id.icon_2);
- textview2.setTypeface(iconfont);
- TextView textview3 = (TextView)findViewById(R.id.icon_3);
- textview3.setTypeface(iconfont);
- TextView textview4 = (TextView)findViewById(R.id.icon_4);
- textview4.setTypeface(iconfont);
这只是同一个页面上有四个 iconfont 的情况,如果是多页面多处地方使用到 iconfont,那就可怕了,难以接受。身为程序员,除了会写代码,还要讲究效率和质量。我开始在想,是否有更好的方案来使用 iconfont。
由于 iconfont 属于一种字体,所以我开始从自定义字体的方向上去探索。首先想到的就是自定义 TextView 组件,这也是我最终选择的方案。但在此之前,我想跟大家分享一个国外的方案,他不考虑界面的布局层级,通过遍历当前页面上所有基于 TextView 的文本组件来设置字体。
- /**
- * Apply specified font for all text views (including nested ones) in the specified
- * root view.
- *
- * @param context
- * Context to fetch font asset.
- * @param root
- * Root view that should have specified font for all it's nested text
- * views.
- * @param fontPath
- * Font path related to the assets folder (e.g. "fonts/YourFontName.ttf").
- */
- public static void applyFont(final Context context, final View root, final String fontName) {
- try {
- if (root instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) root;
- for (int i = 0; i < viewGroup.getChildCount(); i++)
- applyFont(context, viewGroup.getChildAt(i), fontName);
- } else if (root instanceof TextView)
- ((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontName));
- } catch (Exception e) {
- Log.e(TAG, String.format("Error occured when trying to apply %s font for %s view", fontName, root));
- e.printStackTrace();
- }
- }
使用方法:
- FontHelper.applyFont(context, findViewById(R.id.activity_root), "fonts/YourCustomFont.ttf");
一行代码就行了,传入上下文,根布局和字体文件路径三个参数,非常简单粗暴。刚看到这个方法时,我有些惊讶,之所以在这里拿出来跟大家分享,并不是想说这种方法有多好,而是因为我被作者的这种活跃的思维所震撼,这很值得我们去学习,不局限于传统,大胆勇于创新。
好了,不再废话了,说说我的方案——自定义 TextView。
我创建了一个 View,继承系统的 TextView,将其命名为 IconFontTextView。
然后在 res/values 目录下,新建一个 attrs.xml 的资源文件,如果已经存在,就不需要重复创建了。这是我的 attrs.xml 的内容:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <declare-styleable name="IconFontTextView">
- <attr name="value" format="string"/>
- <attr name="fontFile" format="string"/>
- </declare-styleable>
- </resources>
我定义了两个属性,value 填写 iconfont 图标对应的值,fontFile 是字体文件路径。
然后在 IconFontTextView 里添加两个构造方法
- public IconFontTextView(Context context) {
- super(context);
- }
- public IconFontTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
在第二个构造方法里处理属性值,具体代码如下:
- public IconFontTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- if (attrs == null) {
- return;
- }
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IconFontTextView);
- final int N = a.getIndexCount();
- for (int i = 0; i < N; i++) {
- int attr = a.getIndex(i);
- switch (attr) {
- case R.styleable.IconFontTextView_value:
- value = a.getString(attr);
- setText(value);
- Log.d(TAG, "value : " + value);
- break;
- case R.styleable.IconFontTextView_fontFile:
- fontFile = a.getString(attr);
- Log.d(TAG, "fontFile : " + fontFile);
- try {
- Typeface typeface = Typeface.createFromAsset(context.getAssets(), fontFile);
- setTypeface(typeface);
- } catch (Throwable e) {
- }
- break;
- }
- }
- a.recycle();
- }
其实很简单,这样我们就完成了自定义 IconFontTextView 了,接下来讲下怎么使用。在布局文件中,跟普通的 TextView 一样,添加 IconFontTextVIew,
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <com.xhj.huijian.mmcherryuidemo.view.IconFontTextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:fontFile="iconfont/iconfont.ttf"
- android:id="@+id/iconfont_view"
- app:value=""
- />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="搜索"/>
- </LinearLayout>
记得在根布局添加这一行 xmlns:app="http://schemas.android.com/apk/res-auto" ,这样才可以使用自定义属性。紧接着,指定字体文件 fontFile="iconfont/iconfont.ttf",告诉 IconFontTextView 字体文件路径位于 assets 目录下的 iconfont/iconfont.ttf,并给它一个 iconfont 的值,
- app:value=""
- <!--当然,你也可以在 res/values/strings.xml 文件上添加 string 再来引用-->
- app:value="@string/arrow";
这个值对应的图标是一个箭头,
这样直接跑程序就可以看到效果了,是不是方便了很多?因为一般我们不使用 TextView 来监听事件,更多的是让它去负责 View 层的一些展示,而且这里我们也不再需要在 java 代码中去指定加载字体文件,所以根本不需要再去 findViewById 获取实例了,所以可以省去下面的一段代码,
- Typeface iconfont = Typeface.createFromAsset(getAssets(), "iconfont/iconfont.ttf");
- TextView textview1 = (TextView)findViewById(R.id.icon_1);
- textview1.setTypeface(iconfont);
- TextView textview2 = (TextView)findViewById(R.id.icon_2);
- textview2.setTypeface(iconfont);
- ...
IconFontTextView 我们可以抽出来当做一个组件使用,这样以后使用 iconfont 时,将它当做普通的 TextView 来使用,并指定字体文件和图标值,就可以了。
也许你有这样的需求,我想要在运行时去动态的设置 IconFont 的值。就拿我上面那底部栏四个 tab 来说吧,当我选中其中一个 tab 时,我想要它显示选中状态,如果只是改变颜色那就方便多了,如果在改变颜色的同时,还需要改变图标呢?你可能会说,这个问题很容易解决啊,因为 IconFontTextView 是 TextView 的子类,我重新给它一个 value 不就好了吗?是的,可是当你通过 setText("") 后你会发现,图标成这样了
这跟你想要的结果完全不同
这是为什么呢?我就遇到了这个问题,我还发现,当我在 res/values/strings.xml 文件上添加 string 再来使用时却不会出现这个问题,像下面这样就什么问题都没有:
- setText(getString(R.string.wifi));
难道 getString 里面做了什么处理吗?看了源码,没发现什么特别的地方。那好吧,我直接通过 log 打印出来,看看 getString 返回了什么。
我在 string.xml 上添加了
- <string name="iconfont"></string>
然后在 Activity 上添加下面两行测试代码,
- Log.d("tag", getString(R.string.iconfont));
- Log.d("tag", "");
按照以往的经验,这八九不离十跟 unicode 字符有关。把代码稍改一下
- setText("\ue6f2");// "&#x" 替换成 "\u",用 unicode 字符来表示
这样问题就解决了。
OK,最后附上我实现的效果图,
Android 上使用 iconfont 的一种便捷方案的更多相关文章
- Android上掌纹识别第一步:基于OpenCV的6种肤色分割 源码和效果图
Android上掌纹识别第一步:基于OpenCV的6种肤色分割 源码和效果图 分类: OpenCV图像处理2013-02-21 21:35 6459人阅读 评论(8) 收藏 举报 原文链接 ht ...
- Android - 页面返回上一页面的三种方式
今年刚刚跳槽到了新公司,也开始转型做Android,由此开始Android的学习历程. 最近在解很多UI的bug,在解bug过程中,总结了在UI的实现过程中,页面返回上一页面的几种实现方式. 一. 自 ...
- 页面结构化在 Android 上的尝试
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/M45DM5Ix7a2fmrsE8VPvxg 作者:b ...
- 通杀所有系统的硬件漏洞?聊一聊Drammer,Android上的RowHammer攻击
通杀所有系统的硬件漏洞?聊一聊Drammer,Android上的RowHammer攻击 大家肯定知道前几天刚爆出来一个linux内核(Android也用的linux内核)的dirtycow漏洞.此洞可 ...
- Android上dip、dp、px、sp等单位说明(转)
dip device independent pixels(设备独立像素). 不同设备不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这个,不依赖像素. 在 ...
- Android上dip、dp、px、sp等单位说明
Android上dip.dp.px.sp等单位说明 dip device independent pixels(设备独立像素). 不同设备不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA ...
- [转]收集android上开源的酷炫的交互动画和视觉效果:Interactive-animation
原文链接:http://www.open-open.com/lib/view/open1411443332703.html 描述:收集android上开源的酷炫的交互动画和视觉效果. 1.交互篇 2. ...
- 最牛逼android上的图表库MpChart(一) 介绍篇
最牛逼android上的图表库MpChart一 介绍篇 MpChart优点 MpChart是什么 MpChart支持哪些图表 MpChart效果如何 最牛逼android上的图表库MpChart(一) ...
- Android跨进程通信的四种方式
由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于andro ...
随机推荐
- 你晓得吗?大多数企业根本没有做到 DevOps!
作为当代 IT 企业提升效率的葵花宝典,DevOps 对 IT 企业效率的提升有目共睹 ,一时之间各大企业纷纷用提升效率的 DevOps 开发.协作.管理工具武装自己. 对比 2014 年上半年,CS ...
- NetFlow网络流量监测技术的应用和设计(转载)
http://blog.chinaunix.net/uid-20466300-id-1672909.html http://www.cww.net.cn/news/html/2014/12/25/20 ...
- 【20161030la 】总结
就写个题解 1. 生成树(Tree) 有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边组成的圈.在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形.这些 ...
- CollapsingToolbarLayout
CollapsingToolbarLayout作用是提供了一个可以折叠的Toolbar,它继承至FrameLayout,给它设置layout_scrollFlags,它可以控制包含在Collapsin ...
- Android 获取图片资源的4种方式
1. 图片放在sdcard中 Bitmap imageBitmap = BitmapFactory.decodeFile(path) (path 是图片的路径,跟目录是/sdcard) 2. 图片在项 ...
- android学习——环境搭建之HelloWorld
一.在开始Android开发之旅启动之前,首先要搭建环境,然后创建一个简单的HelloWorld.本文的主题如下: 1.1.JDK安装 1.2.Eclipse安装 1.3.Android SDK安装 ...
- Unity 中关于 BuildSetting 中 “Optimize Mesh Data” 选项的“坑”
Unity 在底层默认希望为你做尽可能多的优化,降低使用门槛,比如 BuildSetting 中的 Optimize Mesh Data 选项就是一个典型的例子. 这个选项到底有什么用呢?文档描述为: ...
- 深入设计模式(二)——单例模式(Singleton Pattern)
一.单例模式介绍 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯一实例,它就可以严格地控制客户怎样访问它 ...
- glance was not installed properly
- hbulider 快捷键以及常用
跳转到行 Ctrl + G 页首 Ctrl + Home 页尾 Ctrl + End 下一个选项卡 Ctrl + Tab 上一个 ...