在 Android 中使用自定义字体

Posted

技术标签:

【中文标题】在 Android 中使用自定义字体【英文标题】:Using a custom typeface in Android 【发布时间】:2011-02-27 17:34:45 【问题描述】:

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

所以,

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

【问题讨论】:

***.com/questions/5541058/… 看这篇文章我在这里发布了关于这个问题的完整代码 您可以在主要活动中使用静态变量来保存对嵌入字体的引用。这将导致有一组持久的字体不会被 GC 拾取。 工厂方法。或者一种可以让您查看并设置所有字体和字体设置的方法。 新的支持库 26 现在允许您在 XML 中使用字体。这是如何做到的Fonts in XML 【参考方案1】:

是的,有可能。

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

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>

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_
    android:layout_
    >
    <TextView  android:layout_ 
        android:layout_
        android:text="Hello"/>
    <com.lht.ui.MyTextView  
        android:id="@+id/MyTextView"
        android:layout_ 
        android:layout_
        android:text="Hello friends"
        lht:ttf_name="ITCBLKAD.TTF"
        />   
</LinearLayout>

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);
    


【讨论】:

这可行,但仅适用于TextView。对于从 TextView 继承的每个小部件类,它都需要自定义子类,其中需要相同的功能。 嗨 Manish,感谢您的解决方案。尽管我在使用它时遇到了问题。我不断收到“在包 com.lht.android 中找不到属性 ttf_name 的资源标识符” 这会起作用,但在 ICS 之前,它将为您实例化的每个视图的字体分配内存:code.google.com/p/android/issues/detail?id=9904 解决此问题的一种方法是创建一个全局可访问的所有实例化字体的静态哈希图:code.google.com/p/android/issues/detail?id=9904#c7 为什么我们需要一个循环?,它不应该与单个调用一起工作吗? @AlaksiejN。如果您需要为不同的 TextView 设置不同的字体...【参考方案2】:

有没有办法从 XML?

不,抱歉。您只能通过 XML 指定内置字体。

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

我不知道。

现在有多种选择:

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

Third-party libraries 用于那些不使用appcompat 的人,尽管并非所有人都支持在布局资源中定义字体

【讨论】:

@Codevalley:嗯,在很多方面,更好的答案是不要对自定义字体大惊小怪。它们往往很大,使您的下载量更大,并减少下载并继续使用您的应用的人数。 @CommonsWare:我不一定同意。字体是 UI 样式的一个组成部分,就像你对背景图像等所做的那样。而且大小不一定总是很大。例如,我的字体只有 19.6KB :) @Amit:这个问题没有改变。有很多代码 sn-ps 可以帮助简化在 Java 中将字体应用到 UI 中,但是没有办法从 XML 中完成。 @Amit:“但这是否意味着我必须为自定义字体创建自己的 .ttf 或 .otf 文件?” - 不。您可以使用现有的 TTF/OTF 字体,尽管它们可能并不都有效。这个问题的问题是如何在整个应用程序中应用这些字体,这仍然是不可能的,这就是我在评论中所指的。 这个接受的答案已经过时了,更新或删除它会很好。【参考方案3】:

我以一种更“蛮力”的方式做到了这一点,不需要更改布局 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);
    

更完整的例子见http://github.com/perchrh/FontOverrideExample

【讨论】:

这对我来说是最好的解决方案。 默认对我不起作用。如果我使用等宽然后设置 code code 它适用但不适用于大胆的。我将此代码添加到扩展应用程序的类中。那是正确的地方吗? @P-chan @ChristopherRivera 是的,将它添加到您应用的 Application 类中,确保它在 onCreate 上运行。看看grepcode.com/file/repository.grepcode.com/java/ext/…,我建议您覆盖等宽的附加字段以及上面示例代码中的字段! 好的,我更改了 SERIF 字段,它起作用了 :) This answer 引用了这个答案,并提供了一种更简洁的方法恕我直言。【参考方案4】:

虽然我赞成 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
    

您需要在扩展布局后和 Activity 的 onContentChanged() 方法中在视图上调用此函数。

【讨论】:

差不多。当时,这是在项目时间范围内完成的唯一方法。如果需要,一个方便的快捷方式(:【参考方案5】:

我能够以集中的方式执行此操作,结果如下:

我关注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"));


但是,如果我使用支持包中的活动,例如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"));


我还没有用Fragments 测试过这段代码,但希望它能工作。

我的FontUtils 很简单,它也解决了这里提到的https://code.google.com/p/android/issues/detail?id=9904 的 pre-ICS 问题:

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;


【讨论】:

这应该会得到更多的选票。它可以工作,并且有一个用于演示的屏幕截图【参考方案6】:

嘿,我的应用程序中还需要 2 种不同的字体,用于不同的部件! 我是这样用的:

在我的应用程序类中,我创建了一个静态方法:

public static Typeface getTypeface(Context context, String typeface) 
    if (mFont == null) 
        mFont = Typeface.createFromAsset(context.getAssets(), typeface);
    
    return mFont;

String 字体代表资产文件夹中的 xyz.ttf。 (我创建了一个常量类) 现在您可以在应用中的任何地方使用它:

mTextView = (TextView) findViewById(R.id.text_view);
mTextView.setTypeface(MyApplication.getTypeface(this, Constants.TYPEFACE_XY));

唯一的问题是,对于要使用字体的每个小部件,您都需要这个! 但我认为这是最好的方法。

【讨论】:

【参考方案7】:

使用 pospi 的 建议并像 Richard 那样使用“标签”属性,我创建了一个自定义类,用于加载我的自定义字体并将它们应用于视图他们的标签。

因此,基本上,您不是在属性 android:fontFamily 中设置 TypeFace,而是使用 android:tag attritube 并将其设置为定义的枚举之一。

public class Fonts 
    private AssetManager mngr;

    public Fonts(Context context) 
        mngr = context.getAssets();
    
    private enum AssetTypefaces 
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    

    private Typeface getTypeface(AssetTypefaces font) 
        Typeface tf = null;
        switch (font) 
            case RobotoLight:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Light.ttf");
                break;
            case RobotoThin:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Thin.ttf");
                break;
            case RobotoCondensedBold:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Bold.ttf");
                break;
            case RobotoCondensedLight:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Light.ttf");
                break;
            case RobotoCondensedRegular:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Regular.ttf");
                break;
            default:
                tf = Typeface.DEFAULT;
                break;
        
        return tf;
    
    public void setupLayoutTypefaces(View v) 
        try 
            if (v instanceof ViewGroup) 
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++) 
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                
             else if (v instanceof TextView) 
                if (v.getTag().toString().equals(AssetTypefaces.RobotoLight.toString()))
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoLight));
                else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedRegular.toString())) 
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedRegular));
                else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedBold.toString())) 
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedBold));
                else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedLight.toString())) 
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedLight));
                else if (v.getTag().toString().equals(AssetTypefaces.RobotoThin.toString())) 
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoThin));
                
            
         catch (Exception e) 
            e.printStackTrace();
            // ignore
        
    

在您的活动或片段中,您只需调用

Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);

【讨论】:

【参考方案8】:

我在the blog of Lisa Wray 上找到了一个不错的解决方案。使用新的数据绑定,可以在 XML 文件中设置字体。

@BindingAdapter("bind:font")
public static void setFont(TextView textView, String fontName)
    textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));

在 XML 中:

<TextView
app:font="@`Source-Sans-Pro-Regular.ttf`"
    android:layout_
    android:layout_/>

【讨论】:

你能推荐一个使用数据绑定的例子吗?那将不胜感激。【参考方案9】:

我认为可以有更方便的方法来做到这一点。以下类将为应用程序的所有组件设置自定义字体(每个类都有一个设置)。

/**
 * Base Activity of our app hierarchy.
 * @author SNI
 */
public class BaseActivity extends Activity 

    private static final String FONT_LOG_CAT_TAG = "FONT";
    private static final boolean ENABLE_FONT_LOGGING = false;

    private Typeface helloTypeface;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        helloTypeface = Typeface.createFromAsset(getAssets(), "fonts/<your type face in assets/fonts folder>.ttf");
    

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) 
        View view = super.onCreateView(name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) 
        View view = super.onCreateView(parent, name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    

    protected View setCustomTypeFaceIfNeeded(String name, AttributeSet attrs, View view) 
        View result = null;
        if ("TextView".equals(name)) 
            result = new TextView(this, attrs);
            ((TextView) result).setTypeface(helloTypeface);
        

        if ("EditText".equals(name)) 
            result = new EditText(this, attrs);
            ((EditText) result).setTypeface(helloTypeface);
        

        if ("Button".equals(name)) 
            result = new Button(this, attrs);
            ((Button) result).setTypeface(helloTypeface);
        

        if (result == null) 
            return view;
         else 
            if (ENABLE_FONT_LOGGING) 
                Log.v(FONT_LOG_CAT_TAG, "A type face was set on " + result.getId());
            
            return result;
        
    


【讨论】:

【参考方案10】:

LayoutInflater 的默认实现不支持从 xml 指定字体字体。然而,我已经看到它在 xml 中通过为 LayoutInflater 提供一个自定义工厂来完成,该工厂将从 xml 标记中解析此类属性。

基本结构是这样的。

public class TypefaceInflaterFactory implements LayoutInflater.Factory 

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) 
        // CUSTOM CODE TO CREATE VIEW WITH TYPEFACE HERE
        // RETURNING NULL HERE WILL TELL THE INFLATER TO USE THE
        // DEFAULT MECHANISMS FOR INFLATING THE VIEW FROM THE XML
    



public class BaseActivity extends Activity 

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        LayoutInflater.from(this).setFactory(new TypefaceInflaterFactory());
    

This article 更深入地解释了这些机制以及作者如何尝试以这种方式为字体提供 xml 布局支持。作者实现的代码可以在here找到。

【讨论】:

【参考方案11】:

将自定义字体设置为常规 ProgressDialog/AlertDialog:

font=Typeface.createFromAsset(getAssets(),"DroidSans.ttf");

ProgressDialog dialog = ProgressDialog.show(this, "titleText", "messageText", true);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("message", "id", "android"))).setTypeface(font);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("alertTitle", "id", "android"))).setTypeface(font);

【讨论】:

【参考方案12】:

是的,可以通过覆盖默认字体来实现。 我遵循this 解决方案,它对所有 TextViews 和 ActionBar 文本也很有吸引力,只需进行一次更改。

public class MyApp extends Application 

  @Override
  public void onCreate() 
    TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/Roboto-Regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
  

styles.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/pantone</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
    <item name="android:windowDisablePreview">true</item>
    <item name="android:typeface">serif</item>
</style>

我提到了在我的默认应用程序主题标签中的 styles.xml 中覆盖的默认字体,而不是上面链接中提到的 theme.xml。可以覆盖的默认字体有 serif、sans、monospace 和 normal。

TypefaceUtil.java

public class TypefaceUtil 

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     * @param context to work with assets
     * @param defaultFontNameToOverride for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) 
        try 
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
         catch (Exception e) 
            Log.e("Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
        
    

最初,我不知道要被覆盖的字体是固定的并且是一组定义的值,但最终它帮助我了解了 Android 如何处理字体和字体及其默认值,这是一个不同的点。

【讨论】:

【参考方案13】:

我为此使用 DataBinding(使用 Kotlin)。


设置 BindingAdapter:

BindingAdapter.kt

import android.graphics.Typeface
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.databinding.BindingAdapter
import java.util.*


object BindingAdapters 


    @JvmStatic
    @BindingAdapter("typeface", "typefaceStyle")
    fun setTypeface(v: TextView, tf: Typeface?, style: Int?) 
        v.setTypeface(tf ?: Typeface.DEFAULT, style ?: Typeface.NORMAL)
    

用法:

fragment_custom_view.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="android.graphics.Typeface" />

        <variable
            name="typeface"
            type="android.graphics.Typeface" />

    </data>


    <TextView
        android:id="@+id/reference"
        style="@style/TextAppearance.MaterialComponents.Body1"
        android:layout_
        android:layout_
        android:text="I'm formatted text"
        app:typeface="@typeface"
        app:typefaceStyle="@Typeface.ITALIC" />

</layout>

MyFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)

        binding = FragmentCustomView.bind(view)
        binding.typeface = // some code to get user selected typeface



现在,如果用户选择了新字体,您只需更新绑定值,您设置的所有 TextView app:typeface 都会得到更新。

【讨论】:

【参考方案14】:

我不知道它是否会改变整个应用程序,但我已经设法改变了一些通过这样做无法改变的组件:

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Lucida Sans Unicode.ttf");
Typeface.class.getField("DEFAULT").setAccessible(true);
Typeface.class.getField("DEFAULT_BOLD").setAccessible(true);
Typeface.class.getField("DEFAULT").set(null, tf);
Typeface.class.getField("DEFAULT_BOLD").set(null, tf);

【讨论】:

不幸的是,这不再起作用:java.lang.IllegalAccessException:在 java.lang.reflect.Field.setField(Native Method) 的 java.lang.reflect 中的字段被标记为“final”。 Field.set(Field.java:556)【参考方案15】:

我喜欢 pospi 的建议。为什么不全力以赴使用视图的“tag”属性(您可以在 XML 中指定 - 'android:tag')来指定您无法在 XML 中执行的任何其他样式。我喜欢 JSON,所以我会使用 JSON 字符串来指定键/值集。此类工作 - 只需在您的活动中调用 Style.setContentView(this, [resource id])

public class Style 

  /**
   * Style a single view.
   */
  public static void apply(View v) 
    if (v.getTag() != null) 
      try 
        JSONObject json = new JSONObject((String)v.getTag());
        if (json.has("typeface") && v instanceof TextView) 
          ((TextView)v).setTypeface(Typeface.createFromAsset(v.getContext().getAssets(),
                                                             json.getString("typeface")));
        
      
      catch (JSONException e) 
        // Some views have a tag without it being explicitly set!
      
    
  

  /**
   * Style the passed view hierarchy.
   */
  public static View applyTree(View v) 
    apply(v);
    if (v instanceof ViewGroup) 
      ViewGroup g = (ViewGroup)v;
      for (int i = 0; i < g.getChildCount(); i++) 
        applyTree(g.getChildAt(i));
      
    
    return v;
  

  /**
   * Inflate, style, and set the content view for the passed activity.
   */
  public static void setContentView(Activity activity, int resource) 
    activity.setContentView(applyTree(activity.getLayoutInflater().inflate(resource, null)));
  

显然,您需要处理的不仅仅是字体,以使 JSON 值得使用。

'tag' 属性的一个好处是您可以将其设置为您用作主题的基本样式,从而使其自动应用于您的所有视图。 编辑:这样做会导致在 Android 4.0.3 的通货膨胀期间崩溃。您仍然可以使用样式并将其单独应用于文本视图。

您将在代码中看到一件事 - 有些视图有一个标签,但没有明确设置 - 奇怪的是,它是字符串 'Αποκοπή' - 根据谷歌翻译,它在希腊语中是 'cut' !什么鬼……?

【讨论】:

【参考方案16】:

@majinboo 的答案针对性能和内存管理进行了修改。任何多于一种字体需要相关的 Activity 都可以通过将构造函数本身作为参数来使用此 Font 类。

@Override
public void onCreate(Bundle savedInstanceState)

    Font font = new Font(this);

修改后的字体类如下:

public class Fonts

    private HashMap<AssetTypefaces, Typeface> hashMapFonts;

    private enum AssetTypefaces
    
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    

    public Fonts(Context context)
    
        AssetManager mngr = context.getAssets();

        hashMapFonts = new HashMap<AssetTypefaces, Typeface>();
        hashMapFonts.put(AssetTypefaces.RobotoLight, Typeface.createFromAsset(mngr, "fonts/Roboto-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoThin, Typeface.createFromAsset(mngr, "fonts/Roboto-Thin.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedBold, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Bold.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedLight, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedRegular, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Regular.ttf"));
    

    private Typeface getTypeface(String fontName)
    
        try
        
            AssetTypefaces typeface = AssetTypefaces.valueOf(fontName);
            return hashMapFonts.get(typeface);
        
        catch (IllegalArgumentException e)
        
            // e.printStackTrace();
            return Typeface.DEFAULT;
        
    

    public void setupLayoutTypefaces(View v)
    
        try
        
            if (v instanceof ViewGroup)
            
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++)
                
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                
            
            else if (v instanceof TextView)
            
                ((TextView) v).setTypeface(getTypeface(v.getTag().toString()));
            
        
        catch (Exception e)
        
            e.printStackTrace();
            // ignore
        
    

【讨论】:

【参考方案17】:

为 Xamarin.Android 工作:

类:

public class FontsOverride

    public static void SetDefaultFont(Context context, string staticTypefaceFieldName, string fontAssetName)
    
        Typeface regular = Typeface.CreateFromAsset(context.Assets, fontAssetName);
        ReplaceFont(staticTypefaceFieldName, regular);
    

    protected static void ReplaceFont(string staticTypefaceFieldName, Typeface newTypeface)
    
        try
        
            Field staticField = ((Java.Lang.Object)(newTypeface)).Class.GetDeclaredField(staticTypefaceFieldName);
            staticField.Accessible = true;
            staticField.Set(null, newTypeface);
        
        catch (Exception e)
        
            Console.WriteLine(e.Message);
        
    

应用实现:

namespace SomeAndroidApplication

    [Application]
    public class App : Application
    
        public App()
        

        

        public App(IntPtr handle, JniHandleOwnership transfer)
            : base(handle, transfer)
        

        

        public override void OnCreate()
        
            base.OnCreate();

            FontsOverride.SetDefaultFont(this, "MONOSPACE", "fonts/Roboto-Light.ttf");
        
    

风格:

<style name="Theme.Storehouse" parent="Theme.Sherlock">
    <item name="android:typeface">monospace</item>
</style>

【讨论】:

【参考方案18】:

看起来在Android O中使用自定义字体变得很容易,你基本上可以使用xml来实现这一点。我附上了 Android 官方文档的链接以供参考,希望这能帮助仍然需要此解决方案的人。 Working with custom fonts in Android

【讨论】:

【参考方案19】:

知道从 Android 8.0(API 级别 26)开始您可以use a custom font in XML 可能会很有用。

简单来说,可以通过以下方式实现。

    将字体放入文件夹res/font

    要么在小部件的属性中使用它

&lt;Button android:fontFamily="@font/myfont"/&gt;

或放入res/values/styles.xml

<style name="MyButton" parent="android:Widget.Button">
    <item name="android:fontFamily">@font/myfont</item>
</style>

并将其用作样式

<Button style="@style/MyButton"/>

【讨论】:

【参考方案20】:

在xml文件中直接使用“fontPath”属性。

用于 style.xml

字体/ProximaNovaSemibold.ttf

用于直接布局文件

fontPath="fonts/ProximaNovaBold.ttf"

(注意:不需要在前缀中使用 app/android 属性)

【讨论】:

【参考方案21】:

绝对有可能。 有很多方法可以做到。 最快的方法,用try-catch方法创建条件.. 尝试您的特定字体样式条件,捕获错误,然后定义其他字体样式。

【讨论】:

你能提供一个演示来证明你的回答吗?

以上是关于在 Android 中使用自定义字体的主要内容,如果未能解决你的问题,请参考以下文章

Android使用自定义字体(自定义view)

如何预览自定义字体 Android Studio(使用书法)

在使用xml的android TextView中使用自定义字体

Android应用自定义字体在手机上完全被忽略

如何在 android 应用程序中正确对齐自定义字体

Android开发 如何重写SimpleAdapter,自定义字体的大小?