Android开发 为应用设置自定义字体

转载请注明 作者:田野光 地址:http://blog.csdn.net/lovefish2/article/details/46129527

在应用开发中,或许你会听到设计狮和产品锦鲤这样抱怨,安卓原生的字体太丑啦,傻大笨粗啊,有没有办法换成细体啊…?不幸的是,安卓字体确实傻大笨粗,其次,只有normal和bold,没有light啊.如果更不幸的是,你看不上安卓可以切换的几种字体,一定要使用自己的字体文件时,该怎么办呢?下面介绍三种方法,让应用换上新的字体.

(注: 前两种方法都有局限性,想要替换整个应用字体的,直接看第三种方法, 对Android Lollipop同样支持)


  • 方法一: 自定义view 
    1. 首先, 把你的字体文件放在src/main/assets/fonts 下( /fonts 目录手动新建) 
    2. 自定义view继承TextView, 使用自定义view就可以啦
public class CustomFontTextView extends TextView {

    public CustomFontTextView(Context context) {
        super(context);
        init(context);
    }

    public CustomFontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public CustomFontTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void init(Context context) {
        Typeface newFont = Typeface.createFromAsset(context.getAssets(), "fonts/MyFont.ttf");
        setTypeface(newFont);
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

  • 方法二: 遍历viewGroup 
    我们会发现,第一种方法其实只适用于局部位置修改字体,例如整个布局页面都像要设置自定义字体怎么办呢?我们可以这样做:
   public class FontHelper {

    public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect) {
        if (mContainer == null || mFont == null) return;
        for (int i = 0; i < mContainer.getChildCount(); ++i) {
            final View mChild = mContainer.getChildAt(i);
            if (mChild instanceof TextView) {
                ((TextView) mChild).setTypeface(mFont);
            } else if (mChild instanceof EditText) {
                ((EditText) mChild).setTypeface(mFont);
                //你可以在这里添加自定义字体的其他类型的view
            } else if (mChild instanceof ViewGroup) {
                setAppFont((ViewGroup) mChild, mFont, true);
            } else if (reflect) {
                try {
                    Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
                    mSetTypeface.invoke(mChild, mFont);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

例如我们的布局文件是这样的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="字体 123 ABC"
        android:textColor="@android:color/black"
        android:textSize="40dp"/>

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="字体 123 ABC"
        android:textColor="@android:color/black"
        android:textSize="40dp"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="字体 123 ABC"
            android:textColor="@android:color/black"
            android:textSize="40dp"/>

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="字体 123 ABC"
            android:textColor="@android:color/black"
            android:textSize="40dp"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="字体 123 ABC"
            android:textColor="@android:color/black"
            android:textSize="40dp"
            android:typeface="sans"/>

    </LinearLayout>

</LinearLayout>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

我们可以这样修改字体:

    final Typeface mFont = Typeface.createFromAsset(getAssets(),
                "fonts/Roboto-Light.ttf");
    final ViewGroup mContainer = (ViewGroup) findViewById(
                android.R.id.content).getRootView();
    FontHelper.setAppFont(mContainer, mFont, true);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

  • 方法三: 全局映射替换

其实这个方法才是我们绝大多数数时候想要用的, 一次性替换应用全部字体. 
由于官方并没有提供相应的接口,所以这里我们采用的方式时通过反射,获取Typeface类的静态成员变量,并修改对应的字体文件.

这里根据你使用的主题和API的不同,处理方式也有所区别:

  • 非Theme.Material主题

比如你使用的是Theme.Holo主题,或Theme.AppCompat主题, 首先定义一个工具:

public final class FontsOverride {

    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 | IllegalAccessException e) {
                e.printStackTrace();
            }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

然后在主题里指定使用某种字体(android lollipop设置有区别):

values

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <item name="android:typeface">monospace</item>
    </style>
</resources>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

values-v21

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <item name="android:textAppearance">@style/CustomTextAppearance
        </item>
    </style>

    <style name="CustomTextAppearance">
        <item name="android:typeface">monospace</item>
    </style>
</resources>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

最后在你的Application的onCreate()方法里面使用:

FontsOverride.setDefaultFont(this, "MONOSPACE", "fonts/MyFont.ttf");
 
 
  • 1
  • 使用Theme.Material主题 
    Theme.Material主题是Android 5.0新增的主题
public final class FontsOverride {

    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) {
        //android 5.0及以上我们反射修改Typeface.sSystemFontMap变量
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Map<String, Typeface> newMap = new HashMap<>();
            newMap.put(staticTypefaceFieldName, newTypeface);
            try {
                final Field staticField = Typeface.class
                        .getDeclaredField("sSystemFontMap");
                staticField.setAccessible(true);
                staticField.set(null, newMap);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            try {
                final Field staticField = Typeface.class
                        .getDeclaredField(staticTypefaceFieldName);
                staticField.setAccessible(true);
                staticField.set(null, newTypeface);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
//Material主题默认字体为sans-serif,暂时未找到自定义修改字体的方法
FontsOverride.setDefaultFont(this, "sans-serif", "fonts/Roboto-Light.ttf");
 
 
  • 1
  • 2

  • 其他方法: 如使用第三方库Calligraphy, 我没使用过,如果有感兴趣的可以自己尝试一下. 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值