Android 更换系统字体......

  

原文:http://vision-apps.blogspot.hk/2012/02/android-better-way-to-apply-custom-font.html

在一个应用中,我需要在所有的UI组件中使用客户提供的字体。这听起来似乎是个很稀松平常的任务,不是吗?是的,我当时也是这么想的。然后我震惊了,Android竟然没有提供一个简单优雅的方式来做这件事情!

所以,在这篇文章中我会展示Android提供的默认方法,然后我会分享更加简单优雅的解决方案。

情景

你需要为整个应用替换自定义字体。

解决方案

1)Android默认方法 #1

你可以通过ID查找到View,然后挨个为它们设置字体。在单个View的情况下,它看起来也没有那么可怕。

Typeface customFont = Typeface.createFromAsset(this.getAssets(), "fonts/YourCustomFont.ttf");
TextView view = (TextView) findViewById(R.id.activity_main_header);
view.setTypeface(customFont);

但是在很多TextView、Button等文本组件的情况下,我敢肯定你不会喜欢这个方法的。:D

2)Android默认方法 #2

你可以为每个文本组件创建一个子类,如TextView、Button等,然后在构造函数中加载自定义字体。

public class BrandTextView extends TextView {

      public BrandTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public BrandTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BrandTextView(Context context) {
super(context);
}
public void setTypeface(Typeface tf, int style) {
if (style == Typeface.BOLD) {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont_Bold.ttf"));
} else {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/YourCustomFont.ttf"));
}
}
}

然后只需要将标准的文本控件替换成你自定义的就可以了(例如BrandTextView替换TextView)。

<com.your.package.BrandTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View with custom font"/>
<com.your.package.BrandTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="View with custom font and bold typeface"/>

还有,你甚至可以直接在XML中添加自定义的字体属性。要实现这个,你需要定义你自己的`declare-styleable`属性,然后在组件的构造函数中解析它们。

为了不占篇幅介绍这么基础的东西,这里有一篇不错的文章告诉你怎么自定义控件属性。

http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/

在大多数情况下,这个方法还不赖,并且有一些优点(例如,切换字体粗细等等,字体可以在组件xml文件的typeface属性中定义)。但是我认为这个实现方法还是太重量级了,并且依赖大量的模板代码,为了一个替换字体的简单任务,有点儿得不偿失。

3)我的解决方案

理想的解决方案是自定义主题,然后应用到全局或者某个Activity。 
但不幸的是,Android的`android:typeface`属性只能用来设置系统内嵌的字体,而非用户自定义字体(例如assets文件中的字体)。这就是为什么我们无法避免在Java代码中加载并设置字体。

所以我决定创建一个帮助类,使得这个操作尽可能的简单。使用方法:

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

并且这行代码会用来加载所有的基于TextView的文本组件(TextView、Button、RadioButton、ToggleButton等等),而无需考虑界面的布局层级如何。

标准(左)与自定义(右)字体的用法。

这是怎么做到的?非常简单:

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();
}
}

正如你所看到的,所需要做的仅仅是将基于TextView的文本组件从布局中遍历出来而已。

你可以在这里下载到示例代码,里面有`FontHelper`的具体用法。

译者注

在多个项目中,我都碰到过类似的需求,早期采用的是第二种实现方法,但是缺点在于对于第三方组件,你需要去修改别人的代码,才能实现自定义字体,这恰恰违反了OC(Open & Close)原则,对扩展开放,对修改封闭。

刚看到第三种的时候,也是惊为天人,姑且不说结果,我觉得这种活跃的思路非常重要,很值得学习参考。

但是最后被team里的人否决了,理由是违背组件设计原则,实现方式略嫌粗暴。后来我仔细想想,一个是要做好内存管理(似乎会引起内存问题),视图状态改变,也要重复加载(横竖屏、onResume等),也绝对不是简单的活儿。

所以暂定使用第一种方法,typeface使用单例,需要时设置字体。

我个人觉得第一种还是个体力活,而且到后来,这个代码重复率还是非常高的,这又违背了DRY原则。

在地铁上的时候,突然想到DI(Dependency Inject)。已经有一些DI的框架,如ButterKnife,那写出来应该是这样:

@CustomFont(R.id.textView) TextView textView

or

@InjectView(id=R.id.textView, customFont=true) View anyView
@InjectView(id=R.id.textView, customFont=true, font="fonts/ltfz.ttf") View anyView

这样写出来代码相比重复写setTypeface要好一些。

目前我们的项目还没有使用这类DI框架,等以后引入了,使用第二种注入,写起来应该是很爽的。

保持更新。

参考

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) 
http://ryanhoo.github.io/blog/2014/05/05/android-better-way-to-apply-custom-font/

Android 更换系统字体......的更多相关文章

  1. [Android] 字体使用dp单位避免设置系统字体大小对排版的影响

    [Android] 字体使用dp单位避免设置系统字体大小对排版的影响 以魄族mx3为例,在设置->显示->字体大小中能够选择字号大小例如以下图: 图1. 魄族mx3 会导致软件在有固定定高 ...

  2. Android系统字体规范

    我们在做Android移动APP设计的时候,字号的选择也是很让人头疼,转载一份有关Android系统字体规范,如果在做Android项目的用户应该看看,如果有任何建议欢迎在留言处与我们交流探讨. 主要 ...

  3. Android: 设置 app 字体大小不跟随系统字体调整而变化

    在做 app 内字体大小的需求,类似于 微信中设置字体大小. 那么就需要 app 不跟随系统字体大小调整而变化,找到了两个方法. 方法1: 重写 getResource() 方法,修改 configu ...

  4. Android 系统字体和颜色样式

    Android 字体和颜色 对于能够显示文字的控件(如TextView EditText RadioButton Button CheckBox Chronometer等等),你有时需要控制字体的大小 ...

  5. Linux 桌面系统字体配置要略

    字体显示效果测试 这一段是为了测试宋体字的显示效果,包括宋体里面自带的英文字体,“This is english,how does it look like?”.这一行是小字.后面几个字是加粗的宋体. ...

  6. android 修改系统的dialog样式

    android 修改系统的dialog样式 一.觉得自定义配置文件麻烦?那就来修改系统自定义XML文件来实现修改系统dialog的样式吧. 如果是在XML中样式:首先来说下样式.  在 Style.x ...

  7. Android实现系统下拉栏的消息提示——Notification

    Android实现系统下拉栏的消息提示--Notification 系统默认样式 默认通知(通用) 效果图 按钮 <Button android:layout_width="match ...

  8. Linux 桌面玩家指南:04. Linux 桌面系统字体配置要略

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  9. Android之系统架构

    Android 是Google开发的基于Linux平台的开源手机操作系统.它包括操作系统.用户界面和应用程序 —— 移动电话工作所需的全部软件,而且不存在任何以往阻碍移动产业创新的专有权障碍.Andr ...

随机推荐

  1. java 成神之路

    一.基础篇 1.1 JVM 1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 http://i ...

  2. List小练习

    功能:创建链表节点,删除节点,顺序打印,不改变原结构的情况下分别用STL中的stack实现逆序打印和利用函数递归打印 代码如下: //链表问题struct ListNode {    int m_nV ...

  3. jdbc之二:DAO模式

    详细代码请参见 https://code.csdn.net/jediael_lu/daopattern 1.创建Dao接口. package com.ljh.jasonnews.server.dao; ...

  4. SQLServer 2012 可视化窗口中,设置“时间”默认值为“当前时间"

    最近,需要在SQLServer 2012中,设置datetime的默认值为当前时间. 通过可视化窗口进行设置,而不是将getdate()函数写在sql语句中,也不是将‘2022-2-2 22:22:2 ...

  5. Fragment与Activity

    一个Fragment的实例总是和包含它的Activity直接相关. fragment可以通过getActivity() 方法来获得Activity的实例,然后就可以调用一些例如findViewById ...

  6. [汇编语言]-第八章 div指令,伪指令dd,dup

    1- div除法指令 (1) 除数: 有8位和16位两种,在一个寄存器或内存单元中. (2) 被除数: 默认放在AX和DX或AX中 除数为8位, 被除数为16位, 默认在AX中存放. 除数为16位, ...

  7. Lotto--poj2245

    Lotto Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6605   Accepted: 4185 Description ...

  8. chart

    var pieChart = { width: 600, height: 400, series: [], unit: "kg", chartCanvas: null, selec ...

  9. nginx grok 正则错误的输出情况

    nginx 配置: http { include mime.types; default_type application/octet-stream; log_format main '$http_h ...

  10. Go语言实现-观察者模式

    前前言 这个类经过我的正式投入使用啊,发现不对劲,这样做可能会导致线程死锁 比如你dispatch一个event,然后在这个回调里把那个事件的侦听给remove掉了,那么就会导致线程死锁(这个问题找了 ...