http://ryanhoo.github.io/blog/2014/05/05/android-better-way-to-apply-custom-font/

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

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

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

情景

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

解决方案

1)Android默认方法 #1

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

1
2
3
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等,然后在构造函数中加载自定义字体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)。

1
2
3
4
5
6
7
8
9
<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代码中加载并设置字体。

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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,那写出来应该是这样:

1
@CustomFont(R.id.textView) TextView textView

or

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

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

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

保持更新。

参考

Android:更好的自定义字体方案的更多相关文章

  1. Android中快速实现自定义字体!

    前言:我们都知道,Android中默认的字体是黑体,而大多数app也都是使用的这种字体,但我们发现,大多数app中,个别地方字体非常好看,例如app的标题栏,菜单栏等地方,那他们是怎么做到的呢?有两种 ...

  2. Android控件使用自定义字体

    我们不可能只满足于系统自带的字体(太丑),其实控件自定义字体也很简单.. 1.首先找到该字体的ttf文件. 2.把字体文件放在scr/mian/assets/fonts下,如果没有该路径则自己创建. ...

  3. 【Android 界面效果42】如何自定义字体

    项目里要统一用设计师的字体,android:typeface只支持系统三种字体.有什么比较好的做法? 你需要为整个应用替换自定义字体. 解决方案 1)Android默认方法 #1 你可以通过ID查找到 ...

  4. Android 中使用自定义字体的方法

    1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . <?xml version="1.0 ...

  5. 转--Android中自定义字体的实现方法

    1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . 复制代码 代码如下: <?xml versio ...

  6. Android实现自定义字体

    介绍 最近在看开源项目的时候,发现里面涉及到了自定义字体,虽然自己目前还用不到,但是动手demo笔记记录一下还是有必要的,没准哪天需要到这个功能. 原理 1.其实实现起来非常简单,主要是用到了Type ...

  7. Android 使用自定义字体

    整个项目要使用第三方字体首先将字体文件放到assets文件夹下 因为整个项目要用第三方字体这里我重写了 TextView Button EditText 三个控件 以TextView 为例代码如下   ...

  8. Android Studio设置自定义字体

    Android Studio设置自定义字体 (1)进入设置页面,File->Settings (2)自定义字体Editor->Colors&Fonts->Font (3)点击 ...

  9. Android 开发使用自定义字体

    有时候,系统自带的字体并不能满足我们特殊的需求,这时候就需要引用其他的字体了,可以把下载的字体文件放在 assets 目录下. 自定义字体文件不能使用xml代码读取而应该使用java代码: publi ...

随机推荐

  1. CentOS搭建V~P~N服务,实现虚拟专用网络

    什么是V-P-N V-P-N即虚拟专用网络,它的功能是:在公用网络上建立专用网络,进行加密通讯. V-P-N网关通过对数据包的加密和数据包目标地址的转换实现远程访问.V-P-N有多种分类方式,主要是按 ...

  2. LESS SCSS 预编译语言

      前  言 JRedu LESS  是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量.Mixin.函数等特性,使 CSS 更易维护和扩展. LESS 为 Web 开发者带来了福音,它在 ...

  3. Redis+Keepalived高可用环境部署记录

    Keepalived 实现VRRP(虚拟路由冗余)协议,从路由级别实现VIP切换,可以完全避免类似heartbeat脑裂问题,可以很好的实现主从.主备.互备方案,尤其是无状态业务,有状态业务就需要额外 ...

  4. What is 软件工程

    话说新的一学期,有一门叫软件工程的专业课,作为计算机科学与技术专业的学生,在上课前有几个问题 1.软件工程顾名思义是学软件,可是软件这个词范围还是挺大的,那到底学的是哪方面,是编程?设计APP?还是一 ...

  5. Code Review —— by12061154Joy

    对结对队友刘丽萍的代码进行了复审: 优点: 1,代码逻辑正确,基本能够完全需求 2,用了不少C#自带的函数,第一次写C#,相信是查阅了不少资料,虽然还有很多地方值得优化,不过第一次能做到这样已经很不错 ...

  6. 结对项目 Pair Project

    结对项目 Pair Project 一人编程,一人操作,共同检查. 源码 https://github.com/dpch16303/test/blob/master/%E5%AE%9E%E8%B7%B ...

  7. Vue的router使用

    <div id="app"> <router-link to="/home">home</router-link> < ...

  8. MySQL乐观锁在分布式场景下的实践

    背景 在电商购物的场景下,当我们点击购物时,后端服务就会对相应的商品进行减库存操作.在单实例部署的情况,我们可以简单地使用JVM提供的锁机制对减库存操作进行加锁,防止多个用户同时点击购买后导致的库存不 ...

  9. docker发现端口是tcp6的 导致无法访问前端

    最近偶尔发现一个比较奇怪的现象,netstat 查看监听的服务端口时,却只显示了 tcp6 的监控, 但是服务明明是可以通过 tcp4 的 ipv4 地址访问的,那为什么没有显示 tcp4 的监听呢? ...

  10. 一个ip对应多个域名多个ssl证书配置-Nginx实现多域名证书HTTPS

    一台服务器,两个域名 首先购买https,获取到CA证书,两个域名就得到两套证书 第二步:现在就是Nginx和OpenSSL的安装与配置(这里注意,一般情况下一个IP只支持一个SSL证书,那么我们现在 ...