如何在Android应用程序中为整个应用程序设置自定义字体?

93

在Android应用程序中设置自定义字体是否可能?

我尝试了这里发布的内容,但我不知道我的extends Application类在哪里...

有帮助吗?

编辑:

我尝试了以下操作:

  • 添加资产文件夹并将字体插入其中,如此处所示:

enter image description here

  • 添加一个继承自Application的新类。

  • 从我的AndroidManifest.xml中调用这个新类。

  • 我去了我的样式并添加了它。

MyApp.java:

public class MyApp extends Application {
  @Override
  public void onCreate() {
     super.onCreate();
    FontsOverride.setDefaultFont(this, "DEFAULT", "raleway_regular.ttf");
    //  This FontsOverride comes from the example I posted above
  }
  }

AndroidManifest.xml:

<application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:supportsRtl="true"
      android:name=".MyApp"
      android:theme="@style/AppTheme">
     ....

styles.xml:

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:fontFamily">default</item>
 </style>

但是我的字体仍然没有改变... 有什么想法吗?

然后调用MyApp类。但是对我的字体没有影响...

编辑2: 我意识到在我为按钮设置自定义样式之后,我的按钮应用了自定义字体。这是我的自定义按钮样式:

<style name="MyButtonStyle" parent="Widget.AppCompat.Button">
    <item name="textAllCaps">false</item>
    <item name="android:textAllCaps">false</item>
</style>

现在它看起来是这样的:

enter image description here

所以:我的按钮应用了样式,但是TextView没有。你有任何想法为什么我的自定义字体没有应用于应用程序中的所有项目吗?

你提供的链接对你的问题非常有帮助。 - Androider
我知道,我认为我的解决方案就在那里......但是当我创建一个继承自Application的类时,它不能正常工作,因为它说我的类从未被使用过... - Sonhja
请在我的回答中查看更多链接,它们似乎很有帮助。还有一件事,我的第一个链接确实更好。谢谢。 - Androider
15个回答

92
我所做的事情是:
1:在RES文件夹中添加了“新资源目录”,从下拉菜单中选择RESOURCE TYPE为“字体”,将新目录命名为“font”并保存。 NEW RESOURCE DIRECTORY 2:将我的“custom_font.ttf”添加到刚创建的FONT文件夹中。
3:在STYLES.XML中的应用程序基本主题中添加了我的自定义字体。

STYLES.XML

已完成。

7
您还需要创建一个字体族。之后,它对我有用了。详情请参见:https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html - keybee
对我来说非常好用,甚至没有字体族。 - Julian Alberto
@majov,请查看上面#Rajeesh的回答。那应该会对你有所帮助! - Numanqmr
你可以使用Typeface,但那只适用于单个视图,例如文本视图等。所以我不建议这样做。我不知道其他方法。多搜索一些,肯定会找到的。 :) @majov - Numanqmr
它对Canvas和OpenGL视图无效。 - undefined
显示剩余4条评论

81

编辑

uk.co.chrisjenx:calligraphy库已不再维护,最新的Android版本现在的替代方案是 https://github.com/InflationX/Calligraphy

dependencies {
    implementation 'io.github.inflationx:calligraphy3:3.1.1'
    implementation 'io.github.inflationx:viewpump:2.0.3'
}

将自定义字体添加到 assets/ 目录下。

使用方法:

对于默认字体:

在你的 Application 类中的 #onCreate() 方法中使用 CalligraphyConfig 定义你的默认字体,并将其传递给 CalligraphyInterceptor,然后将其添加到你的 ViewPump 构建器中。

@Override
public void onCreate() {
    super.onCreate();
    ViewPump.init(ViewPump.builder()
        .addInterceptor(new CalligraphyInterceptor(
                new CalligraphyConfig.Builder()
                    .setDefaultFontPath("fonts/Roboto-RobotoRegular.ttf")
                    .setFontAttrId(R.attr.fontPath)
                    .build()))
        .build());
    //....
}

注入到上下文:包装Activity上下文:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase));
}

自定义样式

<style name="TextViewCustomFont">
    <item name="fontPath">fonts/RobotoCondensed-Regular.ttf</item>
</style>

主题

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:textViewStyle">@style/AppTheme.Widget.TextView</item>
</style>

<style name="AppTheme.Widget"/>

<style name="AppTheme.Widget.TextView" parent="android:Widget.Holo.Light.TextView">
    <item name="fontPath">fonts/Roboto-ThinItalic.ttf</item>
</style>

开发者已不再维护以下解决方案


在安卓中有一个很棒的自定义字体库:Calligraphy
这是一个使用示例。

在Gradle中,您需要将此行放入您的应用程序的build.gradle文件中:

        dependencies {
            compile 'uk.co.chrisjenx:calligraphy:2.2.0'
        }

然后创建一个扩展Application类的类并编写以下代码:

        public class App extends Application {
            @Override
            public void onCreate() {
                super.onCreate();

                CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
                                .setDefaultFontPath("your font path")
                                .setFontAttrId(R.attr.fontPath)
                                .build()
                );
            }
        } 
你应该在assets目录下创建一个名为"fonts"的新文件夹(如下所示),所以在这段代码中,"your font path" 应该是 "fonts/SourceSansPro-Regular.ttf"。注意,它只是"fonts..."而不是"/fonts..."或"assets.."。
在活动类中,在onCreate方法之前加入这个方法。
        @Override
        protected void attachBaseContext(Context newBase) {
            super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
        }

最后,你的清单文件应该像这样:

        <application
           .
           .
           .
           android:name=".App">

这将会把整个活动的字体都改变为您选择的字体!它简单而清爽!

在资源文件夹上,您应该右键单击新目录,将其命名为“fonts”。在Finder中将.ttf字体文件放入其中。

进入图像描述

还要不要忘记在attrs.xml中添加以下两行内容。如果您没有attrs.xml文件,请在values.xml中创建新文件。

 <attr format="string" name="fontPath"/> 
    <item name="calligraphy_tag_id" type="id"/>

我正在使用ttf字体,但我非常确定.otf字体也可以使用,并且最佳位置是放在assets文件夹中。 - Rajesh N
将这段代码放在扩展的Application类中:CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() .setDefaultFontPath("fonts/GOTHAM-MEDIUM.ttf") .setFontAttrId(R.attr.fontPath) .build());另外,别忘了在values.xml文件中添加以下两行代码: <attr format="string" name="fontPath"/> <item name="calligraphy_tag_id" type="id"/> - Rajesh N
这仍然不是一种解决方案。我必须记得为我添加的每个新活动添加attachBaseContext(Context newBase)。我期望一种解决方案,可以一次完成所有必要的配置,然后它将在整个应用程序中设置所有TextView的字体。 - Pawan
1
您可以尽可能多地使用片段,以减少活动的数量。 - Rajesh N
1
@Pawan 要做到这一步 - 我定义了一个简单的基类,其中实现了 attachBaseContext(..),然后我想要使用自定义字体的所有项目 Activity 类都继承该基类。 - Gene Bo
显示剩余9条评论

52

编写一个类

public class MyApp extends Application{
// Put the onCreate code as you obtained from the post link you reffered
}

现在下一步是在AndroidManifest.xml文件中为应用程序标签指定应用程序类的名称。在这种情况下,它是MyApp。

<application
android:name=".MyApp"
...
>
...
</application>
无论何时打开该应用程序,MyApp类的onCreate方法都将被调用,字体将被设置。 更新:将字体文件放在assets/fonts/your_font_file.ttf下。
将此行代码放在您的应用程序类(MyApp)的onCreate方法下。
TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/your_font_file.ttf");

字体工具类的源文件

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

        final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Map<String, Typeface> newMap = new HashMap<String, Typeface>();
            newMap.put("serif", customFontTypeface);
            try {
                final Field staticField = Typeface.class
                        .getDeclaredField("sSystemFontMap");
                staticField.setAccessible(true);
                staticField.set(null, newMap);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            try {
                final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
                defaultFontTypefaceField.setAccessible(true);
                defaultFontTypefaceField.set(null, customFontTypeface);
            } catch (Exception e) {
                Log.e(TypefaceUtil.class.getSimpleName(), "Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
            }
        }
    }
}

现在更新您的style.xml文件。

将以下行放入您在清单文件中为您的活动包含的样式中。

  <item name="android:typeface">serif</item>
希望这可以帮到你。

请告诉我您正在测试的Android操作系统版本。我观察到在棒棒糖中,我们需要进行一些额外的调整。 - Nitin Mesta
请查看以下链接,获取Lollipop和之前版本的Android的解决方案:(https://dev59.com/0nE85IYBdhLWcg3wkkbK#36206275) - blueware
它会更改所有应用程序中的字体,包括toast消息和警报对话框,但我不想在此使用自定义字体。 - Adil
2
我有一个疑问,许多制造商允许用户从设置中全局更改字体,那么这种更改是否也会覆盖我们在程序中设置的字体类型? - Bhuro
如何设置多种字体? - Gaurav Mandlik
@GauravMandlik 这样做不行。您需要为每个视图单独设置它。 - Sambhav Khandelwal

21

Android提供了更简单和最佳的解决方案,该解决方案由Support Library 26+在API级别14或以上得到支持。 XML中的字体 它还支持字体族选项。 所需导入: implementation "com.android.support:support-compat:<26+ version>"

按照以下简单步骤应用字体族,在您的应用程序中无需任何障碍即可应用:

  1. res文件夹内创建一个font目录
  2. 将字体文件粘贴在font
  3. 右键单击字体文件夹,然后转到New > Font resource file。 新资源文件窗口将出现。
  4. 添加以下属性
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/lobster_regular" />
    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/lobster_italic" />
</font-family>
  • 在上面的xml文件中,只有在使用支持库时才使用 app。否则,如果您的应用程序目标为API级别26+,可以使用 android
  • 现在转到styles.xml并将以下行放入主样式中。
  • <item name="android:fontFamily">@font/opensans</item>
    
  • 重要提示:只有在使用支持库时才使用 AppCompatActivity
  • 或者使用以下代码来以编程方式设置字体:

    view.setTypeface(ResourcesCompat.getFont(context, R.font.opensans));
    

  • 这种方式适用于当前的Android字体资源功能。谢谢。 - azwar_akbar

    15
    您现在可以使用Android SDK进行操作,正如Android Developer官方YouTube频道在2017年9月发布的视频here所示。 需要API Level 23或更高版本。 如果您需要使用特定字体,请将字体文件放入res/font文件夹,并在TextView中引用它们,使用android:fontFamily属性。 如果您希望在整个应用程序中设置基础字体,请简单地添加以下代码:
    <item name="android:fontFamily">@font/yourfontname</item> 
    

    在你的 styles.xml 中。

    如果需要,您可以从Android Studio中下载并使用自定义字体。所有这些细节都可以在上面的视频中找到。


    此选项适用于API 26及以上版本,不适用于16+版本。 - Perfect Square
    API 23及以上的修复怎么样? - Jeff Bootsholz

    5
    1. 首先,右键单击项目资源,资源名称为res,然后从新选项中选择Android Resource Directory,在Resource type中选择font,并按下OK,这将创建一个字体目录以存放您的字体。

    enter image description here

    1. 将您的.ttf字体文件放在其中。

    enter image description here

    1. Go to project style file which exists in following path res/values/styles.xml and add styles like that:

      <style name="SansSerifFont">
          <item name="android:fontFamily">sans-serif</item>
      </style>
      
      <style name="OpenSansFont">
          <item name="android:fontFamily">@font/open_sans</item>
      </style>
      
      <style name="IranianSansFont">
          <item name="android:fontFamily">@font/iranian_sans</item>
      </style>
      
      <style name="BYekanFont">
          <item name="android:fontFamily">@font/b_yekan</item>
      </style>
      
      <style name="DroidArabicFont">
          <item name="android:fontFamily">@font/droid_arabic</item>
      </style>
      
    2. Go to your code and write a class like this for changing whole app font:

      public class AppFontManager {
      
          private AppCompatActivity appCompatActivity;
      
          public static final String SANS_SERIF_APP_FONT = "sans_serif";
          public static final String B_YEKAN_APP_FONT = "b_yekan";
          public static final String DROID_ARABIC_APP_FONT = "droid_arabic";
          public static final String IRANIAN_SANS_APP_FONT = "iranian_sans";
          public static final String OPEN_SANS_APP_FONT = "open_sans";
      
          public AppAppearanceManager(AppCompatActivity appCompatActivity) {
              this.appCompatActivity = appCompatActivity;
          }
      
          public void setFont(String fontName){
      
              switch (fontName){
      
                  case SANS_SERIF_APP_FONT:{
                      appCompatActivity.getTheme().applyStyle(R.style.SansSerifFont, true);
                      break;
                  }
      
                  case B_YEKAN_APP_FONT:{
                      appCompatActivity.getTheme().applyStyle(R.style.BYekanFont, true);
                      break;
                  }
      
                  case DROID_ARABIC_APP_FONT:{
                      appCompatActivity.getTheme().applyStyle(R.style.DroidArabicFont, true);
                      break;
                  }
      
                  case IRANIAN_SANS_APP_FONT:{
                  appCompatActivity.getTheme().applyStyle(R.style.IranianSansFont, true);
                      break;
                  }
      
                  case OPEN_SANS_APP_FONT:{
                      appCompatActivity.getTheme().applyStyle(R.style.OpenSansFont, true);
                      break;
                  }
              }
          }
      }
      

      Because fonts should separately set for each activities, I prefer write a class for it for more clean code.

    3. Then call the class method before activity onCreate super.onCreate(savedInstanceState):

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          new AppAppearanceManager(this).setFont(APP_DEFAULT_FONT);
          super.onCreate(savedInstanceState);
      // Do you stuff there...
      }
      

      Remember that if you want changing font during runtime, write a chosen font in SharedPreferences or etc, then pass chosen font to all activities setFont like above.


    4

    这是我如何将字体ttf应用于整个App的方法:

    1. 在res目录下创建一个名为font的文件夹

    2. 将字体ttf(例如varela.ttf)复制到此font文件夹中

    3. 在AndroidManifest.xml中检查主题

        <application
            android:theme="@style/Theme.MyAppTheme">
            <activity
                ......
            </activity>
        </application>
    

    4. 进入values目录下的themes.xml文件,在AndroidManifest.xml中引用的基础主题样式内添加具有item标签的字体。

        <resources xmlns:tools="http://schemas.android.com/tools">
            <!-- Base application theme. -->
            <style name="Theme.MyAppTheme"
              parent="Theme.MaterialComponents.DayNight.DarkActionBar">
              <item name="android:fontFamily">@font/varela</item>
            </style>
        </resources>
    

    2

    在主文件夹中创建assets文件夹,在其中添加fonts文件夹,将.ttf文件添加到fonts文件夹中。

    在您的应用程序主题中添加以下内容:

     <item name="android:fontFamily">@font/roboto_regular</item>
        <item name="fontFamily">@font/roboto_regular</item>
    

    创建一个名为TypefaceUtil的类。
     import android.content.Context;
    import android.graphics.Typeface;
    import android.util.Log;
    
    import java.lang.reflect.Field;
    
    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","Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
            }
        }
    }
    

    在应用程序类或启动器活动中调用它

            TypefaceUtil.overrideFont(getApplicationContext(), "fonts/roboto_regular.ttf", "fonts/roboto_regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
    

    2

    看起来你在styles.xml中使用了android:fontFamily而不是android:typeface

    尝试替换为

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:fontFamily">default</item>
    </style>
    

    使用

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:typeface">default</item>
    </style>
    

    1
    我该如何将它与我的应用程序连接起来?谁会从中继承? - Sonhja
    通过扩展Application,您只需创建您的应用程序的Application类的子类。因此,您不需要连接它。 - Suhas
    它从未被使用过,是什么提示了呢? - Suhas
    你是否也按照同一篇Stack Overflow帖子中的建议,在你的应用程序的“res->values->styles.xml”文件的“<style name =”AppTheme“ parent =”AppBaseTheme“>”内部添加了“<item name =”android:typeface“>default</item>”行? - Suhas
    是的,而且它也不起作用。那只是我在编辑中输入的... :( - Sonhja

    2
    如果您遵循第一个主题,这应该可以工作:这里 是的,使用反射。这个方法可行(基于这个答案):
    (注意:这是一个解决方案,因为没有支持自定义字体,所以如果您想改变这种情况,请在此处点赞以支持android问题)。请注意:不要在该问题上留下“我也是”评论,每个已点赞的人在您这样做时都会收到一封电子邮件。所以请只“点赞”它。
    import java.lang.reflect.Field;
    import android.content.Context;
    import android.graphics.Typeface;
    
    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 e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    }
    

    然后,您需要重载一些默认字体,例如在应用程序类中:

    public final class Application extends android.app.Application {
        @Override
        public void onCreate() {
            super.onCreate();
            FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
            FontsOverride.setDefaultFont(this, "MONOSPACE", "MyFontAsset2.ttf");
            FontsOverride.setDefaultFont(this, "SERIF", "MyFontAsset3.ttf");
            FontsOverride.setDefaultFont(this, "SANS_SERIF", "MyFontAsset4.ttf");
        }
    }
    

    当然,如果您使用的是相同的字体文件,您可以改进此操作,只需加载一次即可。
    但是,我倾向于仅覆盖一个字体,例如“MONOSPACE”,然后设置样式以在整个应用程序中强制使用该字体。
    <resources>
        <style name="AppBaseTheme" parent="android:Theme.Light">
        </style>
    
        <!-- Application theme. -->
        <style name="AppTheme" parent="AppBaseTheme">
            <item name="android:typeface">monospace</item>
        </style>
    </resources>
    

    API 21 Android 5.0

    我已经调查了评论中的报告,发现它与主题android:Theme.Material.Light不兼容。

    如果该主题对您不重要,请使用较旧的主题,例如:

    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="android:typeface">monospace</item>
    </style>
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接