在Android中使用自定义字体

Cod*_*ley 111 xml layout fonts android

我想为我正在创建的Android应用程序使用自定义字体.
我可以从Code中单独更改每个对象的字体,但我有数百个.

所以,

  • 有没有办法从XML中执行此操作?[设置自定义字体]
  • 有没有办法从一个地方的代码中做到这一点,说整个应用程序和所有组件应该使用自定义字体而不是默认字体?

小智 109

对的,这是可能的.

您必须创建一个扩展文本视图的自定义视图.

attrs.xmlvalues文件夹中:

<resources>
    <declare-styleable name="MyTextView">
        <attr name="first_name" format="string"/>
        <attr name="last_name" format="string"/>
        <attr name="ttf_name" format="string"/>
    </declare-styleable>
</resources>
Run Code Online (Sandbox Code Playgroud)

main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lht="http://schemas.android.com/apk/res/com.lht"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello"/>
    <com.lht.ui.MyTextView  
        android:id="@+id/MyTextView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello friends"
        lht:ttf_name="ITCBLKAD.TTF"
        />   
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

MyTextView.java:

package com.lht.ui;

import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class MyTextView extends TextView {

    Context context;
    String ttfName;

    String TAG = getClass().getName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            Log.i(TAG, attrs.getAttributeName(i));
            /*
             * Read value of custom attributes
             */

            this.ttfName = attrs.getAttributeValue(
                    "http://schemas.android.com/apk/res/com.lht", "ttf_name");
            Log.i(TAG, "firstText " + firstText);
            // Log.i(TAG, "lastText "+ lastText);

            init();
        }

    }

    private void init() {
        Typeface font = Typeface.createFromAsset(context.getAssets(), ttfName);
        setTypeface(font);
    }

    @Override
    public void setTypeface(Typeface tf) {

        // TODO Auto-generated method stub
        super.setTypeface(tf);
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 这将有效,但是在ICS之前它将为您实例化的每个视图分配字体内存:http://code.google.com/p/android/issues/detail?id = 9999解决此问题的方法是创建所有实例化字体的全局可访问静态哈希映射:http://code.google.com/p/android/issues/detail?id = 9994#c7 (17认同)
  • 这将工作,但仅适用于`TextView`.对于需要相同功能的`TextView`继承的每个widget类,它都需要自定义子类. (4认同)

Com*_*are 80

有没有办法从XML中执行此操作?

不,对不起 您只能通过XML指定内置字体.

有没有办法从一个地方的代码中做到这一点,说整个应用程序和所有组件应该使用自定义字体而不是默认字体?

不是我知道的.

现在有很多种选择:

  • 如果您使用Android SDK中的字体资源和反向移植 appcompat

  • 那些不使用的第三方库appcompat虽然不是全部都支持在布局资源中定义字体

  • @CommonsWare:我不一定同意.字体是UI样式的一个组成部分,就像你使用背景图像等一样.尺寸不一定总是很大.例如,我的情况下的字体只有19.6KB :) (22认同)
  • @Codevalley:嗯,在很多方面,更好的答案是不要对自定义字体大惊小怪.它们往往很大,使您的下载量更大,并减少了下载和继续使用您的应用程序的人数. (4认同)
  • @Amit:这个问题没有变化.有很多代码片段可以帮助简化在Java中将字体应用到UI,但是没有办法从XML中完成. (2认同)

Per*_*den 49

我是以更"强力"的方式做到这一点,不需要更改布局xml或活动.

在Android 2.1到4.4版本上测试过.在应用程序启动时,在Application类中运行此命令:

private void setDefaultFont() {

    try {
        final Typeface bold = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_FONT_FILENAME);
        final Typeface italic = Typeface.createFromAsset(getAssets(), DEFAULT_ITALIC_FONT_FILENAME);
        final Typeface boldItalic = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_ITALIC_FONT_FILENAME);
        final Typeface regular = Typeface.createFromAsset(getAssets(),DEFAULT_NORMAL_FONT_FILENAME);

        Field DEFAULT = Typeface.class.getDeclaredField("DEFAULT");
        DEFAULT.setAccessible(true);
        DEFAULT.set(null, regular);

        Field DEFAULT_BOLD = Typeface.class.getDeclaredField("DEFAULT_BOLD");
        DEFAULT_BOLD.setAccessible(true);
        DEFAULT_BOLD.set(null, bold);

        Field sDefaults = Typeface.class.getDeclaredField("sDefaults");
        sDefaults.setAccessible(true);
        sDefaults.set(null, new Typeface[]{
                regular, bold, italic, boldItalic
        });

    } catch (NoSuchFieldException e) {
        logFontError(e);
    } catch (IllegalAccessException e) {
        logFontError(e);
    } catch (Throwable e) {
        //cannot crash app if there is a failure with overriding the default font!
        logFontError(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更完整的示例,请参阅http://github.com/perchrh/FontOverrideExample

  • [这个答案](http://stackoverflow.com/a/16883281/1221537)引用了这个答案,并提供了一个更清洁的方法imho. (3认同)
  • 默认不适合我.如果我使用等宽,然后设置`code` <style name ="AppTheme"parent ="AppBaseTheme"> <item name ="android:typeface"> monospace </ item> </ style>`code`它有效,但不适用胆大.我在扩展Application的类中添加了此代码.那是正确的地方吗?@ P-CHAN (2认同)
  • @ChristopherRivera是的,将它添加到你的应用程序的Application类中,确保它在onCreate上运行.看看http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/graphics/Typeface.java/我建议你覆盖一下monospace的附加字段以及上面的示例代码中的字段! (2认同)
  • 好的,我改变了字段SERIF,它工作:) (2认同)

pos*_*spi 36

虽然我赞同Manish的答案是最快和最有针对性的方法,但我也看到了天真的解决方案,它们只是递归地遍历视图层次结构并依次更新所有元素的字体.像这样的东西:

public static void applyFonts(final View v, Typeface fontToSet)
{
    try {
        if (v instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) v;
            for (int i = 0; i < vg.getChildCount(); i++) {
                View child = vg.getChildAt(i);
                applyFonts(child, fontToSet);
            }
        } else if (v instanceof TextView) {
            ((TextView)v).setTypeface(fontToSet);
        }
    } catch (Exception e) {
        e.printStackTrace();
        // ignore
    }
}
Run Code Online (Sandbox Code Playgroud)

在膨胀布局和Activity的onContentChanged()方法之后,您需要在视图上调用此函数.


M-W*_*eEh 23

我能够以集中的方式做到这一点,结果如下:

在此输入图像描述

Activity如果我需要自定义字体,我有以下内容并从中扩展:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater.Factory;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(String name, Context context,
                AttributeSet attrs) {
            View v = tryInflate(name, context, attrs);
            if (v instanceof TextView) {
                setTypeFace((TextView) v);
            }
            return v;
        }
    });
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs); 
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用支持包中的活动,FragmentActivity那么我使用它Activity:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontFragmentActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

// we can't setLayout Factory as its already set by FragmentActivity so we
// use this approach
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    View v = super.onCreateView(name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(View parent, String name, Context context,
        AttributeSet attrs) {
    View v = super.onCreateView(parent, name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs);
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}
Run Code Online (Sandbox Code Playgroud)

我还没有用Fragments 测试过这段代码,但希望它会起作用.

FontUtils很简单也解决了此处提到的ICS前问题https://code.google.com/p/android/issues/detail?id=9904:

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Typeface;

public class FontUtils {

private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

public static Typeface getFonts(Context context, String name) { 
    Typeface typeface = TYPEFACE.get(name);
    if (typeface == null) {
        typeface = Typeface.createFromAsset(context.getAssets(), "fonts/"
                + name);
        TYPEFACE.put(name, typeface);
    }
    return typeface;
}
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该得到更多的选票.它有效,它有一个演示截图 (2认同)

Inf*_*0re 10

嘿我在我的应用程序中还需要2种不同的字体用于不同的小部件!我用这种方式:

在我的Application类中,我创建了一个静态方法:

public static Typeface getTypeface(Context context, String typeface) {
    if (mFont == null) {
        mFont = Typeface.createFromAsset(context.getAssets(), typeface);
    }
    return mFont;
}
Run Code Online (Sandbox Code Playgroud)

String字体表示资产文件夹中的xyz.ttf.(我创建了一个常量类)现在你可以在你的应用程序中随处使用它:

mTextView = (TextView) findViewById(R.id.text_view);
mTextView.setTypeface(MyApplication.getTypeface(this, Constants.TYPEFACE_XY));
Run Code Online (Sandbox Code Playgroud)

唯一的问题是,对于要使用Font的每个小部件都需要这个!但我认为这是最好的方式.