公司客户端开发分为IOS组、Android组与HTML5组,在字体统一上就出现了问题,需要Android采用IOS的字体,这样就只能替换APP字体了。现在才发现Android开发有时候就是很苦逼,特别是拥有IOS开发组的公司,因为不管在设计风格、逻辑、效果都要以IOS为准,其实大家上网搜索下就会发现许多Android的效果都是模仿IOS的(例如横向滑动删除),这种趋势让身为Android程序员情何以堪啊,让Google情何以堪啊。

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

使用方法:
1、请在Application中添加以下代码替换全局字体FontUtils.getInstance().replaceSystemDefaultFontFromAsset(this, "fonts/xxx.ttf");2、请在设置主题代码中添加以下代码monospace
   
package core.util;import android.app.Application;import android.content.Context;import android.graphics.Typeface;import android.support.annotation.NonNull;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.lang.ref.SoftReference;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class FontUtils {        private static final String TAG = FontUtils.class.getSimpleName();    private Map> mCache = new HashMap<>();    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;    }    /**     * 

Replace the font of specified view and it's children

* @param root The root view. * @param fontPath font file path relative to 'assets' directory. */ public void replaceFontFromAsset(@NonNull View root, @NonNull String fontPath) { replaceFont(root, createTypefaceFromAsset(root.getContext(), fontPath)); } /** *

Replace the font of specified view and it's children

* @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(@NonNull View root, @NonNull String fontPath, int style) { replaceFont(root, createTypefaceFromAsset(root.getContext(), fontPath), style); } /** *

Replace the font of specified view and it's children

* @param root The root view. * @param fontPath The full path to the font data. */ public void replaceFontFromFile(@NonNull View root, @NonNull String fontPath) { replaceFont(root, createTypefaceFromFile(fontPath)); } /** *

Replace the font of specified view and it's children

* @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(@NonNull View root, @NonNull String fontPath, int style) { replaceFont(root, createTypefaceFromFile(fontPath), style); } /** *

Replace the font of specified view and it's children with specified typeface

*/ private void replaceFont(@NonNull View root, @NonNull 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 = 0; i < viewGroup.getChildCount(); ++i) { replaceFont(viewGroup.getChildAt(i), typeface); } } // else return } /** *

Replace the font of specified view and it's children with specified typeface and text style

* @param style One of {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC}, {@link Typeface#BOLD_ITALIC} */ private void replaceFont(@NonNull View root, @NonNull Typeface typeface, int style) { if (root == null || typeface == null) { return; } if (style < 0 || style > 3) { 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 = 0; i < viewGroup.getChildCount(); ++i) { replaceFont(viewGroup.getChildAt(i), typeface, style); } } // else return } /** *

Create a Typeface instance with specified font file

* @param fontPath font file path relative to 'assets' directory. * @return Return created typeface instance. */ private Typeface createTypefaceFromAsset(Context context, String fontPath) { SoftReference typefaceRef = mCache.get(fontPath); Typeface typeface = null; if (typefaceRef == null || (typeface = typefaceRef.get()) == null) { typeface = Typeface.createFromAsset(context.getAssets(), fontPath); typefaceRef = new SoftReference<>(typeface); mCache.put(fontPath, typefaceRef); } return typeface; } private Typeface createTypefaceFromFile(String fontPath) { SoftReference typefaceRef = mCache.get(fontPath); Typeface typeface = null; if (typefaceRef == null || (typeface = typefaceRef.get()) == null) { typeface = Typeface.createFromFile(fontPath); typefaceRef = new SoftReference<>(typeface); mCache.put(fontPath, typefaceRef); } return typeface; } /** *

Replace system default font. Note:you should also add code below to your app theme in styles.xml.

* {@code monospace} *

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.

* @param context {@link Context Context} * @param fontPath font file path relative to 'assets' directory. */ public void replaceSystemDefaultFontFromAsset(@NonNull Context context, @NonNull String fontPath) { replaceSystemDefaultFont(createTypefaceFromAsset(context, fontPath)); } /** *

Replace system default font. Note:you should also add code below to your app theme in styles.xml.

* {@code monospace} *

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.

* @param context {@link Context Context} * @param fontPath The full path to the font data. */ public void replaceSystemDefaultFontFromFile(@NonNull Context context, @NonNull String fontPath) { replaceSystemDefaultFont(createTypefaceFromFile(fontPath)); } /** *

Replace system default font. Note:you should also add code below to your app theme in styles.xml.

* {@code monospace} *

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.

*/ private void replaceSystemDefaultFont(@NonNull 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或许在多线程环境下性能表现会更好些。

下面是该工具类作者的GitHub:https://github.com/whinc

   
   

更多相关文章

  1. Android 命名规范 (提高代码可以读性)
  2. Android的开源隐忧:品牌稀释 代码分裂
  3. linux和windows平台下,如何下载android sdk的源代码
  4. Android应用程序内部启动Activity过程(startActivity)的源代码分析
  5. [Android] 开心消消乐代码(写的比较简单)
  6. 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 缓存策略 | LruC
  7. Android UI 之TextView控件中可选择的属性列表,开发中经常用到的
  8. Android Button控件的使用

随机推荐

  1. Android(安卓)5.0 Material Design酷炫风
  2. Android基于bmob后端云实现数据读取
  3. 彩蛋篇 之 瞧瞧各大公司官网彩蛋 ~
  4. 用剪纸类比Android(安卓)View的绘制流程
  5. Android(安卓)中实现在界面上右划返回上
  6. Android应用程序主要组件知识小结
  7. android在更新ADT以后报java.lang.NoClas
  8. 设置AlertDialog的列表样式
  9. github上开源的优秀android项目
  10. [Android初级]android单元测试之Activity