Android修改字體樣式

在Android實際開發(fā)中根據(jù)UI的設(shè)計圖,經(jīng)常要去改變系統(tǒng)默認(rèn)的字體樣式
這樣做會使apk變大很多啊
而且為什么android要使用ios的字體-_-#

單獨設(shè)置字體樣式

(1)Android系統(tǒng)提供了幾種字體樣式可供選擇

通過設(shè)置typeface屬性或者fontFamily屬性設(shè)置
typeface屬性:

  • normal
  • serif
  • sans
  • monospace

fontFamily屬性:

  • casual
  • cursive
  • serif
  • monospace
  • sans-serif
  • sans-serif-condensed
  • serif-monospace
  • sans-serif-smallcaps
※typeface和fontFamily區(qū)別

android:typeface屬性是增加API1
android:fontFamily在API16(4.1)中添加了屬性

※當(dāng)同時設(shè)置typeface和fontFamily時,只有fontFamily生效

查看一波TextView的源碼

    private void setTypefaceFromAttrs(String familyName, int typefaceIndex, int styleIndex) {
        Typeface tf = null;
        if (familyName != null) {
            tf = Typeface.create(familyName, styleIndex);
            if (tf != null) {
                setTypeface(tf);
                return;
            }
        }
        switch (typefaceIndex) {
            case SANS:
                tf = Typeface.SANS_SERIF;
                break;

            case SERIF:
                tf = Typeface.SERIF;
                break;

            case MONOSPACE:
                tf = Typeface.MONOSPACE;
                break;
        }

        setTypeface(tf, styleIndex);
    }

從方法setTypefaceFromAttrs()看咨油,如果你有set fontFamily屬性狈醉,那么typefaceattribute將被忽略岁歉。

這邊會發(fā)現(xiàn)這樣設(shè)置typeface和fontFamily屬性對中文不生效箕母,這時候就需要引用外部的字體樣式(這里谷歌設(shè)計規(guī)范推薦使用NOTO字體https://www.google.com/get/noto/

(2)使用字體樣式文件設(shè)置(otf吓妆,ttf文件都可以)

在assets下新建一個fonts文件嗜历,把字體樣式文件放進去

在代碼中設(shè)置

AssetManager mgr = getAssets();
Typeface tf = Typeface.createFromAsset(mgr, "fonts/NotoSansCJKsc-Black.otf");
tv_1.setTypeface(tf);

批量設(shè)置字體樣式

(1)自定義TextView

public class CustomTextView extends TextView
{

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

    //重寫設(shè)置字體方法
    @Override
    public void setTypeface(Typeface tf)
    {
        tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/NotoSansCJKsc-Light.otf");
        super.setTypeface(tf);
    }
}

之后在XML布局文件中使用CustomTextView代替TextView

    <com.test.fontfamily.CustomTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="6dp"
        android:text="自定義字體"
        android:textSize="24dp"
        />

(2)更換整個App的字體

思路:遍歷找到所有的TextView然后替換字體
百度了一下找到下面工具類

package com.test.fontfamily;

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;

/**
 * Created by Administrator on 2017/10/24.
 */

public class FontUtils
{

    private static final String TAG = FontUtils.class.getSimpleName();
    private Map<String, SoftReference<Typeface>> 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;
    }

    /**
     * <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(@NonNull View root, @NonNull 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(@NonNull View root, @NonNull 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(@NonNull View root, @NonNull 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(@NonNull View root, @NonNull 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(@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
    }

    /**
     * <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(@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
    }

    /**
     * <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);
            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);
            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(@NonNull Context context, @NonNull 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(@NonNull Context context, @NonNull 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(@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();
        }
    }
}

閱讀源碼發(fā)現(xiàn)這里面主要方法有

  • replaceFont()

替換一個頁面中的所有字體
用遞歸的方式去查找view是否是TextView或者TextView的子類宣渗,然后進行替換
不過這種方法效率不高
用法:
在頁面中
FontUtils.getInstance().replaceFontFromAsset(View root, String fontPath)

  • modifyObjectField()

替換App所有字體
利用反射替換系統(tǒng)默認(rèn)字體
用法:
a.新建一個BaseApplication繼承Application在onCreate方法中
FontUtils.getInstance().replaceSystemDefaultFontFromAsset(this,"fonts/NotoSansCJKsc-Thin.otf");
b.在AppTheme中添加
<item name="android:typeface">monospace</item>
c.清單文件中使用BaseApplication

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抖所,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痕囱,更是在濱河造成了極大的恐慌田轧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞍恢,死亡現(xiàn)場離奇詭異傻粘,居然都是意外死亡,警方通過查閱死者的電腦和手機帮掉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門弦悉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蟆炊,你說我怎么就攤上這事稽莉。” “怎么了涩搓?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵污秆,是天一觀的道長。 經(jīng)常有香客問我缩膝,道長混狠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任疾层,我火速辦了婚禮将饺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痛黎。我一直安慰自己予弧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布湖饱。 她就那樣靜靜地躺著掖蛤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪井厌。 梳的紋絲不亂的頭發(fā)上蚓庭,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音仅仆,去河邊找鬼器赞。 笑死,一個胖子當(dāng)著我的面吹牛墓拜,可吹牛的內(nèi)容都是我干的港柜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夏醉!你這毒婦竟也來了爽锥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤畔柔,失蹤者是張志新(化名)和其女友劉穎氯夷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靶擦,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡肠槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奢啥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘴拢,死狀恐怖桩盲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情席吴,我是刑警寧澤赌结,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站孝冒,受9級特大地震影響柬姚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜庄涡,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一量承、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧穴店,春花似錦撕捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至球凰,卻和暖如春狮腿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呕诉。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工缘厢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人义钉。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓昧绣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捶闸。 傳聞我的和親對象是個殘疾皇子夜畴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 版權(quán)聲明:本賬號發(fā)布文章均來自公眾號饭望,承香墨影(cxmyDev)讨彼,版權(quán)歸承香墨影所有泻蚊。每周會統(tǒng)一更新到這里撰糠,如果喜...
    承香墨影閱讀 2,840評論 0 10
  • RelativeLayout 第一類:屬性值為true可false android:layout_centerHr...
    兀兀沙彌閱讀 2,991評論 0 15
  • android更換字體 最近在研究android端字體替換需求验毡,發(fā)現(xiàn)還是有很多方式來替換字體升熊。 一虚青,前言 Andr...
    六_六閱讀 3,783評論 0 3
  • Android功能強大菱涤,界面華麗苞也,但是眾多的布局屬性就害苦了開發(fā)者,下面這篇文章結(jié)合了網(wǎng)上不少資料.第一類:屬性值...
    HangChen閱讀 4,873評論 0 24
  • 無眠 —落羽 無眠秋色漸意濃 無眠夜色現(xiàn)已深 無眠行人正履匆 無眠睡人今無意 ………… 無眠何時落眠人粘秆?
    你的一個遠(yuǎn)方朋友閱讀 218評論 0 3