zoukankan      html  css  js  c++  java
  • 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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <string name="icons">�</string>  

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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 指定字体

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 (先不考虑选中状态的情况),

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 指定字体

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 的文本组件来设置字体。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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. }  

    使用方法:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. FontHelper.applyFont(context, findViewById(R.id.activity_root), "fonts/YourCustomFont.ttf");  

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

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

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

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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 里添加两个构造方法

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public IconFontTextView(Context context) {  
    2.       super(context);  
    3.   }  
    4.   
    5.   public IconFontTextView(Context context, AttributeSet attrs) {  
    6.       super(context, attrs);  
    7.   }  


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

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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.                       }  
    25.                       break;  
    26.               }  
    27.           }  
    28.           a.recycle();  
    29.       }  

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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 的值,

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. app:value="�"  
    2. <!--当然,你也可以在 res/values/strings.xml 文件上添加 string 再来引用-->  
    3. app:value="@string/arrow";  

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



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

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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 再来使用时却不会出现这个问题,像下面这样就什么问题都没有:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. setText(getString(R.string.wifi));  

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

    我在 string.xml 上添加了

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <string name="iconfont">�</string>  

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

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. Log.d("tag", getString(R.string.iconfont));  
    2. Log.d("tag", "�");  



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

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. setText("ue6f2");// "&#x" 替换成 "u",用 unicode 字符来表示  

    这样问题就解决了。

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

  • 相关阅读:
    WF4.0 Beta1 自定义跟踪
    WF4.0 Beta1 流程设计器与Activity Designer
    新版本工作流平台的 (二) 权限算法(组织结构部分)
    WF4.0 Beta1 WorkflowInvoker
    WF4.0 基础篇 (十) Collection 集合操作
    WF4.0 基础篇 (十五) TransactionScope 事物容器
    WF4.0 基础篇 (六) 数据的传递 Arguments 参数
    WF4B1 的Procedural Activity 之InvokeMethod , InvokeMethod<T> 使用
    WF4.0 Beta1 异常处理
    WF4.0 Beta1 变量 Variables
  • 原文地址:https://www.cnblogs.com/dongweiq/p/5730212.html
Copyright © 2011-2022 走看看