Android替换APP字体 — Typeface

  APP字体的思路一般都会想到自定义控件(TextView、EditView),但是项目中会有很多种控件,就算用也会承担一些风险和资源的消耗,主要是这种思路太死板了,就考虑Android底层应该在字体设置上有放开的方法,然后可以通过Application对控件进行过滤与替换,通过一番搜索果然有所发现,下面贴出代码:

  1、请在Application中添加以下代码替换全局字体

// 字体放在 assets 文件夹下
FontUtils.getInstance().replaceSystemDefaultFontFromAsset(this, "fonts/xxx.ttf"); // .otf 字体文件也可

  2、请在设置主题代码中添加以下代码

  主题代码为 application 中的theme属性的 style 里面。

<item name="android:typeface">monospace</item>

  3、新建文件FontUtils.java

 package com.test.bean;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map; import android.app.Application;
import android.content.Context;
import android.graphics.Typeface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; public class FontUtils { private Map<String, SoftReference<Typeface>> mCache = new HashMap<String, SoftReference<Typeface>>();
private static FontUtils sSingleton = null; public static Typeface DEFAULT = Typeface.DEFAULT; // disable instantiate
private FontUtils() {} public static FontUtils getInstance() {
// double check
if (sSingleton == null) {
synchronized(FontUtils.class) {
if (sSingleton == null) {
sSingleton = new FontUtils();
}
}
}
return sSingleton;
} /**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath font file path relative to 'assets' directory.
*/
public void replaceFontFromAsset(View root, String fontPath) {
replaceFont(root, createTypefaceFromAsset(root.getContext(), fontPath));
} /**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath font file path relative to 'assets' directory.
* @param style One of {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}
*/
public void replaceFontFromAsset(View root, String fontPath, int style) {
replaceFont(root, createTypefaceFromAsset(root.getContext(), fontPath), style);
} /**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath The full path to the font data.
*/
public void replaceFontFromFile(View root, String fontPath) {
replaceFont(root, createTypefaceFromFile(fontPath));
} /**
* <p>Replace the font of specified view and it's children</p>
* @param root The root view.
* @param fontPath The full path to the font data.
* @param style One of {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}
*/
public void replaceFontFromFile(View root, String fontPath, int style) {
replaceFont(root, createTypefaceFromFile(fontPath), style);
} /**
* <p>Replace the font of specified view and it's children with specified typeface</p>
*/
private void replaceFont(View root, Typeface typeface) {
if (root == null || typeface == null) {
return;
} if (root instanceof TextView) { // If view is TextView or it's subclass, replace it's font
TextView textView = (TextView)root;
// Extract previous style of TextView
int style = Typeface.NORMAL;
if (textView.getTypeface() != null) {
style = textView.getTypeface().getStyle();
}
textView.setTypeface(typeface, style);
} else if (root instanceof ViewGroup) { // If view is ViewGroup, apply this method on it's child views
ViewGroup viewGroup = (ViewGroup) root;
for (int i = ; i < viewGroup.getChildCount(); ++i) {
replaceFont(viewGroup.getChildAt(i), typeface);
}
} // else return
} /**
* <p>Replace the font of specified view and it's children with specified typeface and text style</p>
* @param style One of {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC}
*/
private void replaceFont(View root, Typeface typeface, int style) {
if (root == null || typeface == null) {
return;
}
if (style < || style > ) {
style = Typeface.NORMAL;
} if (root instanceof TextView) { // If view is TextView or it's subclass, replace it's font
TextView textView = (TextView)root;
textView.setTypeface(typeface, style);
} else if (root instanceof ViewGroup) { // If view is ViewGroup, apply this method on it's child views
ViewGroup viewGroup = (ViewGroup) root;
for (int i = ; i < viewGroup.getChildCount(); ++i) {
replaceFont(viewGroup.getChildAt(i), typeface, style);
}
} // else return
} /**
* <p>Create a Typeface instance with specified font file</p>
* @param fontPath font file path relative to 'assets' directory.
* @return Return created typeface instance.
*/
private Typeface createTypefaceFromAsset(Context context, String fontPath) {
SoftReference<Typeface> typefaceRef = mCache.get(fontPath);
Typeface typeface = null;
if (typefaceRef == null || (typeface = typefaceRef.get()) == null) {
typeface = Typeface.createFromAsset(context.getAssets(), fontPath);
typefaceRef = new SoftReference<Typeface>(typeface);
mCache.put(fontPath, typefaceRef);
}
return typeface;
} private Typeface createTypefaceFromFile(String fontPath) {
SoftReference<Typeface> typefaceRef = mCache.get(fontPath);
Typeface typeface = null;
if (typefaceRef == null || (typeface = typefaceRef.get()) == null) {
typeface = Typeface.createFromFile(fontPath);
typefaceRef = new SoftReference<Typeface>(typeface);
mCache.put(fontPath, typefaceRef);
}
return typeface;
} /**
* <p>Replace system default font. <b>Note:</b>you should also add code below to your app theme in styles.xml. </p>
* {@code <item name="android:typeface">monospace</item>}
* <p>The best place to call this method is {@link Application#onCreate()}, it will affect
* whole app font.If you call this method after view is visible, you need to invalid the view to make it effective.</p>
* @param context {@link Context Context}
* @param fontPath font file path relative to 'assets' directory.
*/
public void replaceSystemDefaultFontFromAsset(Context context, String fontPath) {
replaceSystemDefaultFont(createTypefaceFromAsset(context, fontPath));
} /**
* <p>Replace system default font. <b>Note:</b>you should also add code below to your app theme in styles.xml. </p>
* {@code <item name="android:typeface">monospace</item>}
* <p>The best place to call this method is {@link Application#onCreate()}, it will affect
* whole app font.If you call this method after view is visible, you need to invalid the view to make it effective.</p>
* @param context {@link Context Context}
* @param fontPath The full path to the font data.
*/
public void replaceSystemDefaultFontFromFile(Context context, String fontPath) {
replaceSystemDefaultFont(createTypefaceFromFile(fontPath));
} /**
* <p>Replace system default font. <b>Note:</b>you should also add code below to your app theme in styles.xml. </p>
* {@code <item name="android:typeface">monospace</item>}
* <p>The best place to call this method is {@link Application#onCreate()}, it will affect
* whole app font.If you call this method after view is visible, you need to invalid the view to make it effective.</p>
*/
private void replaceSystemDefaultFont(Typeface typeface) {
modifyObjectField(null, "MONOSPACE", typeface);
} private void modifyObjectField(Object obj, String fieldName, Object value) {
try {
Field defaultField = Typeface.class.getDeclaredField(fieldName);
defaultField.setAccessible(true);
defaultField.set(obj, value); } catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

  核心代码在:replaceFont方法,替换TextView的字体,那大家就会疑问了,这个工具类只替换了Textview的字体,那如果用了EditView、RadioButton等呢。大家可以看下那些控件的父类,它们都是继承TextView,这样就豁然开朗,细节果然决定成败啊。整个工具类在字体替换的效率上都有所体现,采用软引用和HashMap缓存策略大大降低替换时的资源消耗,考虑的确很全面,并采用反射机制对Typeface进行设置达到换字体的目的。

  这个工具类的确有许多值得学习的地方,比如在单例设置是采用了synchronized 摒弃了懒汉的模式,在资源使用上用到了SoftReference  软引用,在缓存上用了HashMap,最后采用反射赋值,这几点都是可圈可点。如果将缓存的HashMap换成ConcurrentHashMap或许在多线程环境下性能表现会更好些。

Android替换APP字体 — Typeface的更多相关文章

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

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

  2. 【Android初级】使用TypeFace设置TextView的文字字体(附源码)

    在Android里面设置一个TextView的文字颜色和文字大小,都很简单,也是一个常用的基本功能.但很少有设置文字字体的,今天要分享的是通过TypeFace去设置TextView的文字字体,布局里面 ...

  3. 我的Android进阶之旅------>关于使用Android Studio替换App的launcher图标之后仍然显示默认的ic_launcher图标的解决方法

    前言 最近做了一个App,之前开发该App的时候一直以来都是默认的launcher图标启动的, 今天美工换了一个App的launcher 图标,因此在Android Studio中将默认的lanche ...

  4. Android实现自定义字体

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

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

    Android 更换系统字体...... 原文:http://vision-apps.blogspot.hk/2012/02/android-better-way-to-apply-custom-fo ...

  6. 【Android端 APP GPU过度绘制】GPU过度绘制及优化

    一.Android端的卡顿 Android端APP在具体使用的过程中容易出现卡顿的情况,比如查看页面时出现一顿一顿的感受,切换tab之后响应很慢,或者具体滑动操作的时候也很慢. 二.卡顿的原因 卡顿的 ...

  7. Android开发App工程结构搭建

    本文算是一篇漫谈,谈一谈关于android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的角 ...

  8. iOS和Android的app界面设计规范(转)

    记录一下iOS和Andoird的界面设计规范,方便进行标准的产品设计,并与设计师顺畅沟通 iOS篇 界面尺寸 设备 分辨率 状态栏高度 导航栏高度 标签栏高度 iPhone6 plus 1242×22 ...

  9. Android手机app启动的时候第一个Activity必须是MainActivity吗

    原文:Android手机app启动的时候第一个Activity必须是MainActivity吗 Android手机APP启动的第一个Activity是可以自己设置的,不是必须的MainActivity ...

随机推荐

  1. Appium Android Bootstrap源码分析之控件AndroidElement

    通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...

  2. 项目笔记---CSharp图片处理

    原文:项目笔记---CSharp图片处理 项目笔记---CSharp图片处理 最近由于项目上需要对图片进行二值化处理,就学习了相关的图片处理上的知识,从开始的二值化的意义到动态阀值检测二值化等等,并用 ...

  3. 多线程下HashMap的死循环是如何产生的

    前言 HashMap不是线程安全的,如果需要在多线程环境中使用Map,那么我们可以使用ConcurrentHashmap. 1.举例说明: package com.test; import java. ...

  4. 更改MYSQL数据库不区分大小写表名

    今天郁闷死了,在LINUX下调一个程序老说找不到表,但是我明明是建了表的,在MYSQL的命令行下也可以查到,为什么程序就找不到表呢? 后来请教了一个老师才搞定,原来是LINUX下的MYSQL默认是要区 ...

  5. ps入门教程:裁剪工具、修复画笔工具、图章工具的使用

    本节课程主要内容:学习裁剪工具.污点修复画笔工具.修复画笔工具.修补工具.套索工具.红眼工具.仿制图章工具和图案图 章工具的应用.----------------------------------- ...

  6. 【值得收藏】Mathematica数值计算工具的学习资料汇编【可免费下载】

    Mathematica学习教程 Mathematica是一款科学计算软件,很好地结合了数值和符号计算引擎.图形系统.编程语言.文本系统.和与其他应用程序的高级连接.Mathematica与Matlab ...

  7. Jetbrains 系列神器

    PRODUCTS IntelliJ IDEA ReSharper WebStorm PhpStorm PyCharm RubyMine AppCode YouTrack TeamCity dotTra ...

  8. Js模块模式

    模块模式 索引 引子 什么是模块模式 命名空间模式 声明依赖 私有和特权成员 即时函数 揭示模块模式 结语 引子 这篇算是对第9篇中内容的发散和补充,当时我只是把模块模式中的一些内容简单的归为函数篇中 ...

  9. queue,指针求最短路的区别

    这里以spfa为例://都用邻接表存边: 指针: int h=1,t=1; q[h]=x; while(h<=t){ int u=q[h]; vis[u]=0; for(int i=head[u ...

  10. [译]反-反汇编 & 混淆 #1: 苹果没有遵循自己制定的Mach-O规范?

    原文地址:http://reverse.put.as/2012/02/02/anti-disassembly-obfuscation-1-apple-doesnt-follow-their-own-m ...