原文地址:http://www.codefrom.com/c/249
当我第一次接触到Icon Font时,我就喜欢上了它。用字体文件取代图片,对于个人开发者或者缺少UI的项目,无疑是雪中送炭!本文将对自己在Android开发中使用Icon Font遇到的问题及解决方法做一个记录,同时希望能够帮助可能有需要的你。
准备工作
首先给出我看的比较多两个网站:
icomoon https://icomoon.io/app/#/select
阿里icon font字库 http://www.iconfont.cn/
字体图标在Web端使用的比较多了,而关于在Android上使用Icon Font可以参考http://www.iconfont.cn/help/iconuse.html这个地方,可以下载Android的demo运行一下。
简单使用
复制字体文件到项目
assets
目录;打开 iconfont 目录中的 demo.html,找到图标相对应的 HTML 实体字符码;
-
打开
res/values/strings.xml
,添加 string 值;<string name="icons">㘅 㖭 㖮 㖯</string>
-
打开
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)findViewById(R.id.like); textview.setTypeface(iconfont); }
以上步骤来自:http://www.iconfont.cn/help/iconuse.html,我也是根据上面的步骤学习的
问题来了
如果上面的使用方法已经满足了你的需求,那么下面的问题你可能不会遇到。
在我的项目中,用到了Android的自定义View来制作公共的头部Topbar,这个Topbar上就用到了一些小图标(恐怕现在很少看到没有公用头部或者头部没有小图标的应用了吧~-.-)。
在意识到Android可以使用Icon Font之前,我就是发挥了我艺术家潜力的PS功底……它的效果是这样的(求不吐槽!):
正如你们看到的,不光是图标画的不怎么好(其实感觉还可以…),配上上问题也很大(之前没想使用蓝色作为背景的)……因此,当我长了见识之后,果断选择了Icon Font,原因是:1、图标库的比我画的好看,获取方便,勾选->下载一步到位;2、颜色/大小都可以通过代码轻松控制。
那么,前面一些很普通的自定义View的步骤就不多说了:定义attr、继承RelativeLayout等……毕竟是个改造过程。
先看看我选的一些图标:
strings.xml
是这样写的:
<string name="menu">󰆄</string>
<string name="search">󰅼</string>
<string name="message">󰅬</string>
<string name="share">󰅭</string>
<string name="backtop">󰄬</string>
<string name="setting">󰈛</string>
<string name="fire">󰇳</string>
<string name="left">󰄕</string>
<string name="right">󰄖</string>
<string name="up">󰄗</string>
<string name="down">󰄘</string>
<string name="refresh">󰅽</string>
<string name="liked">󰅰</string>
<string name="unliked">󰁔</string>
<string name="logout"></string>
<string name="me">󰅂</string>
<string name="comment">󰈗</string>
<string name="delete">󰈊</string>
<string name="update"></string>
<string name="about"></string>
<string name="commit"></string>
<string name="original"></string>
<string name="hot"></string>
<string name="choice"></string>
接下来就是Topbar的修改了,因为要写的是公用的头部,所以头部左右的TextView的Text需要通过自定义属性设置并获取。
以首页头部的左边Menu图标为例,调用的布局文件中时这样写的custom:LeftText="@string/menu"
,对应的是上面我选的第一个图标。
在Topbar中,通过TypedArray
获取LeftText
属性,代码是这样的:
ta.getText(R.styleable.Topbar_LeftText)
改动并不算大,完了之后运行起来,期待着Icon Font的特效!But……是的,报错了,并且控制台打印了一段很奇怪的log:
这个问题困扰了我很久,网上查到的都是JNI什么鬼相关的,接触Android以来,仅仅看过一两篇JNI的文章,远谈不上去写这个东西了。所以,这个问题是内部机制导致的异常,于是,又是网上找了好久,始终得不到答案,包括尝试字符转换,但是只要程序获取这个string时,就是异常。
初步解决
后来,我看着这段logstring: ' '
,想着能否试试把引号部分的内容复制,使用在线Unicode转码试试,得到转码后的值,得到了这个东西\uDB80\uDD84
。我将这个值设置给我Topbar左边的TextView的Text上,上面的menu图标显示了。
然后,我发现将引号部分的内容复制,在java代码中使用“”将他们包起来,奇迹发生了,看到了"\uDB80\uDD84"
这个东西。
接着,我尝试着使用\uDB80\uDD84
去替换strings.xml
中的󰆄
,也就是这样:
<string name="menu">\uDB80\uDD84</string>
再次运行,成功了!
这时,我想到了一个办法:先将我所有的Icont Font写到一个string,然后必然得到一串很长的Unicode码的报错。那么我在调用布局文件这样写:custom:LeftText="menu"
,然后在获取到这个值的时候,弄一个switch,如果是menu那么我就赋值为\uDB80\uDD84
:
public static String getFontIcon(CharSequence name) {
String res = name.toString();
switch (res) {
case "menu":
return "\uDB80\uDD84";
case "search":
return "\uDB80\uDD7C";
case "message":
return "\uDB80\uDD6C";
case "share":
return "\uDB80\uDD6D";
case "backtop":
return "\uDB80\uDD2C";
case "setting":
return "\uDB80\uDE1B";
case "fire":
return "\uDB80\uDDF3";
case "left":
return "\uDB80\uDD15";
case "right":
return "\uDB80\uDD16";
case "up":
return "\uDB80\uDD17";
case "down":
return "\uDB80\uDD18";
case "refresh":
return "\uDB80\uDD7D";
case "liked":
return "";
case "unliked":
return "\uDB80\uDC54";
case "logout":
return "\uE733";
case "me":
return "\uDB80\uDD42";
case "comment":
return "\uDB80\uDE17";
case "delete":
return "\uDB80\uDE0A";
case "update":
return "\uE70C";
case "about":
return "\uE613";
case "commit":
return "\uE848";
case "original":
return "\uE609";
case "hot":
return "\uE642";
case "choice":
return "\uE66D";
case "article":
return "\uE60B";
case "video":
return "\uE674";
case "topic":
return "\uDB80\uDD84";
case "more":
return "\uDB80\uDD70";
default:
break;
}
return res;
}
上面的这段代码并没有写完整比如liked没有值,因为,我本以为每个icon都是\uDB80\uDD84
这样两段组成的,但是如你所见,里面出现了\uE70C
这样的(这是我通过手动赋值得到的……)。同时,如果我到时候增加了icon,我的代码又需要修改,这是个很蠢的办法。
深入探讨
上面发现update图标对应的是Unicode编码是\uE70C
,而strings.xml
中它是
,相信你也发现了,他们的后面部分是一样的E70C
,而对于menu图标strings.xml
中对应的是󰆄
。结合其他几个,可以发现一个共通点:
开头的string值和
开头的string值不同,并且
对应的Unicode值与自己后半部分一样。
做个猜想, 开头的string值不需要转换即可使用!!!
试验了下,和猜想的一样,结果如下:
那么,为了避免上面那种愚蠢的办法,我多么希望所有的Icon都能使以 
开头!
但是阿里的Icon Font生成的TTF似乎并不支持自己定义string值,也不知道他们是怎么想的!
而[icomoon]()在这方面就做的很好,不仅生成的都是开头的,还支持自定义编辑:
不过,两个网站都使用过的话,我发现阿里的图标更多,并且更适用于国内的开发。
终极解决
当然,要使用阿里的Icon Font库,也不是完全没办法。我的意思是,如果你运气好,生成的都是
开头的话…也不是没有可能的…
不过不靠运气的方法就是,你先要拥有一款字体编辑软件,我使用的是FontLab Studio(Mac版 隐蔽的下载地址,win版应该也有)。
那么,打开ttf文件之后,
选择Names mode
选择不为E开头的字符(一般不是E就是F)
按照E开头的方法修改,别忘记点Apply(这个地方请参考下面的 <自带strong标签>写时感</strong>,我有话要说)
然后点击File -> Generate Font 将生成的font覆盖assert下面的原字体文件
这时候,以图中的“原创”图标为例,你可以使用\uE609
设置为string值,也可以写成
的形式。
写时感
昨晚只是初略解决了这个问题,进入到深入探讨阶段就想着写这篇文章了,一个是为了记录,一个是可能其他人也会碰到这个问题(至少我没找到网上其他人的分析和我需要的解决方案)。庆幸的是,写文章之前搞定了 伪 终极解决方案。
之所以称之为伪解决方案,是因为我在写这篇文章的时候才进一步发现问题的原因:
开头的stirng值比
少一位。
这是我在进行FontLab Studio部分截图的时候观察到的。并且“突发奇想”地,尝试将setting图标的F0218
改成F001
,同时,strings.xml
中使用\uF001
或者
作为setting的string值,最后一样显示了!(如你所见上面截图中还有一个E001
的setting图标,那是我尝试伪终极解决方案时测试用的。)
所以,一切的根本原因在于我使用的ttf字体中的Unicode值的位数差别,也许这是手机的限制导致的问题,更深入的原因我不想继续研究也应该研究不出个所以然来。
当然,这并不代表出现NewStringUTF
的问题就是我上面的出的那个“结论”,上面的结论暂且只适合我在使用阿里的Icon Font时出现的问题总结。
有时间会往git上传一个demo,虽然之前某一篇我也说过类似的话却无动于衷。
看来,写文章不光可以装x,写的时候也可能更深入的看到一些东西、学到一些东西!
什么?通篇不见装x情节?那么……昨晚为了这个问题到了4点半,能解决实在值得;今天得到终极解决方案,并领悟到了一些东西等等的什么鬼,这些都不是重点。
重点是,希望这篇文章能解决你所碰到的问题或者对你有任何帮助。
本文后续变更请关注:http://www.codefrom.com/c/249
更多技术文章,欢迎访问http://www.codefrom.com/获取
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。