Android AppAndroid开发经验谈Android开发

Android APP更换字体策略精要

2017-10-20  本文已影响176人  肖丹晨

前言
近期项目需要在我们的APP中使用指定的字体库。经过搜集资料,研读源码,和别人探讨请教,最终产出了一些比较好的方案。不敢专享,写成文章分享出来,希望对大家的实际开发工作有所帮助。喜欢探讨Android开发技术的同学可以加学习小组QQ群: 193765960

本文只总结了较优方案,其他诸如自定义textView类,遍历layout_root_view这样的方案,作者认为限制较大,使用麻烦,就不在这里介绍了,感兴趣的朋友请自行百度。

版权归作者所有,如有转发,请注明文章出处:https://xiaodanchen.github.io/archives/

Android字体机制介绍

关键类:

TextView的字体显示机制

先看一下TextView的构造方法:

public TextView(Context context);
public TextView(Context context, @Nullable AttributeSet attrs);
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr);
public TextView(
            Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);

Textview的字体设置逻辑:
1)查看xml中是否设置了TextAppearance属性,如果设置了就判断外观中是否设置了字体。否则就执行第二步。
2)查看xml中是否设置了Typeface属性,指明了字体。否则执行第三步
3)使用系统的默认样式:defStyleRes

所以,假如我们的xml中对字体没有做设置,要是想要修改字体又不想修改xml,那么我们就要想其他办法了。
我最终的方案(方案一)是在APP的theme中去设置修改系统的默认样式(最终走到这个思路上是经过了比较酸爽的经过的,就不在这里细说了)。

方案一(底层方案):通过反射机制,修改Typeface类的字体库引用

第一步:通过反射机制修改Typeface字体指向的字体库到我们的字体库。

import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;
 
public final class FontsUtils {
 
  public static void setDefaultFont(Context context,
      String staticTypefaceFieldName, String fontAssetName) {
      final Typeface regular = Typeface.createFromAsset(context.getAssets(),
  fontAssetName);
      replaceFont(staticTypefaceFieldName, regular);
  }

  protected static void replaceFont(String staticTypefaceFieldName,
    final Typeface newTypeface) {
    try {
        final Field staticField = Typeface.class
        .getDeclaredField(staticTypefaceFieldName);
        staticField.setAccessible(true);
        staticField.set(null, newTypeface);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
  }
}
FontsUtils.setDefaultFont(this, "DEFAULT", "fonts/FZLTHJW.TTF");
FontsUtils.setDefaultFont(this, "MONOSPACE", "fonts/FZLTHJW.TTF");
FontsUtils.setDefaultFont(this, "SERIF", "fonts/FZLTHJW.TTF");
FontsUtils.setDefaultFont(this, "SANS_SERIF", "fonts/FZLTHJW.TTF");

第二步:修改APP theme的默认属性。

<style name="AppTheme" parent="AppBaseTheme">
    <item name="android:textViewStyle">@style/FontTextviewstyle</item>
    <item name="android:buttonStyle">@style/FontButtonstyle</item>
    <item name="editTextStyle">@style/FontEditTextstyle</item>
    <item name="android:radioButtonStyle">@style/FontradioButtonstyle</item>
</style>
 
<style name="FontTextviewstyle" parent="android:style/Widget.TextView">
    <item name="android:textAppearance">@style/FontTextAppearance</item>
</style>
<style name="FontButtonstyle" parent="android:style/Widget.Button">
    <item name="android:textAppearance">@style/FontTextAppearance</item>
</style>
<style name="FontradioButtonstyle" parent="android:style/Widget.CompoundButton.RadioButton">
    <item name="android:textAppearance">@style/FontTextAppearance</item>
</style>
<style name="FontEditTextstyle" parent="Widget.AppCompat.EditText">
    <item name="android:textAppearance">@style/FontTextAppearance</item>
</style>
<style name="FontTextAppearance" parent="@android:style/TextAppearance">
        <item name="android:typeface">monospace</item>
</style>

总结:

方案二(顶层方案):自定义布局加载器,在加载layout_xml时对view tree的 view做字体的逻辑处理

    private void replaceFont() {
        final Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/fangzheng.ttf");
        LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory() {
               
            @Override
            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
                // TODO Auto-generated method stub
                AppCompatDelegate delegate = getDelegate();
                View view = delegate.createView(parent, name, context, attrs);
                if(view != null ){
                    if(view instanceof TextView){
                        ((TextView)view).setTypeface(typeface);
                    }else if(view instanceof Button){
                        ((Button)view).setTypeface(typeface);
                    }else if(view instanceof RadioButton){
                        ((RadioButton)view).setTypeface(typeface);
                    }
                }
                return view;
            }
        });
    }
    
/**
*  BaseActivity.java
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
    replaceFont();//注意需要在super方法之前调用,否则会报异常
    super.onCreate(savedInstanceState);
}

总结:

上一篇下一篇

猜你喜欢

热点阅读