【问题标题】:Is it possible to set a custom font for entire of application?是否可以为整个应用程序设置自定义字体?
【发布时间】:2011-02-12 07:14:44
【问题描述】:

我需要为我的整个应用程序使用某种字体。我有相同的 .ttf 文件。 是否可以在应用程序启动时将其设置为默认字体,然后在应用程序的其他地方使用它?设置后,如何在布局 XML 中使用它?

【问题讨论】:

标签: java android android-fonts


【解决方案1】:

是的,可以将字体设置为整个应用程序。

完成此操作的最简单方法是将所需字体与您的应用程序打包在一起。

为此,只需在项目根目录中创建一个 assets/ 文件夹,然后将您的字体(在 TrueType 或 TTF 形式)在资产中。

例如,您可以创建 assets/fonts/ 并将您的 TTF 文件放入其中。

public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);

Typeface face=Typeface.createFromAsset(getAssets(), "fonts/HandmadeTypewriter.ttf");
tv.setTypeface(face);
}
}

【讨论】:

    【解决方案2】:

    虽然这不适用于整个应用程序,但它适用于 Activity,并且可以重复用于任何其他 Activity。感谢@FR073N,我更新了我的代码以支持其他视图。我不确定ButtonsRadioGroups 等的问题,因为这些类都扩展了TextView,所以它们应该可以正常工作。我为使用反射添加了一个布尔条件,因为它看起来很老套,并且可能会显着影响性能。

    注意:正如所指出的,这不适用于动态内容!为此,可以使用 onCreateViewgetView 方法调用此方法,但需要额外的努力。

    /**
     * Recursively sets a {@link Typeface} to all
     * {@link TextView}s in a {@link ViewGroup}.
     */
    public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
    {
        if (mContainer == null || mFont == null) return;
    
        final int mCount = mContainer.getChildCount();
    
        // Loop through all of the children.
        for (int i = 0; i < mCount; ++i)
        {
            final View mChild = mContainer.getChildAt(i);
            if (mChild instanceof TextView)
            {
                // Set the font if it is a TextView.
                ((TextView) mChild).setTypeface(mFont);
            }
            else if (mChild instanceof ViewGroup)
            {
                // Recursively attempt another ViewGroup.
                setAppFont((ViewGroup) mChild, mFont);
            }
            else if (reflect)
            {
                try {
                    Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
                    mSetTypeface.invoke(mChild, mFont); 
                } catch (Exception e) { /* Do something... */ }
            }
        }
    }
    

    然后要使用它,您将执行以下操作:

    final Typeface mFont = Typeface.createFromAsset(getAssets(),
    "fonts/MyFont.ttf"); 
    final ViewGroup mContainer = (ViewGroup) findViewById(
    android.R.id.content).getRootView();
    HomeActivity.setAppFont(mContainer, mFont);
    

    希望对您有所帮助。

    【讨论】:

    • 我认为最好的方法是覆盖 textview。如果您的应用程序有很多视图,则此方法可能会有些多余。
    • 它可以...但根据 Android 规范,您希望避免布局深度超过 4 个级别和 100 个视图宽度(即使这样也不少)。递归在性能方面可能很糟糕,但即使您的布局是 20 级,这也并不重要。
    • 我同意它对性能没有明显影响,我并不是想说你的答案是错误的。如果不需要的话,我只是不喜欢循环浏览布局中的每个视图。此外,通过扩展 TextView,如果您想以另一种方式修改外观,您只需在一个地方更改代码。
    • 只是想补充一下,上面的代码只涵盖了 TextViews 但 ListViews、Alerts、Toasts、Map Markers 等仍将使用系统字体。
    • 伙计们,这个函数有 3 个参数,当你递归调用它时,你传递了 2 个参数???哪个是正确的?什么是反射??
    【解决方案3】:

    我建议扩展 TextView,并始终在 XML 布局中或任何需要 TextView 的地方使用自定义 TextView。在您的自定义 TextView 中,覆盖 setTypeface

    @Override
    public void setTypeface(Typeface tf, int style) {
        //to handle bold, you could also handle italic or other styles here as well
        if (style == 1){
            tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans700.otf");
        }else{
            tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans500.otf");
        }
        super.setTypeface(tf, 0);
    }
    

    【讨论】:

      【解决方案4】:

      我还建议扩展 TextView 和其他控件,但我最好考虑在构造中设置字体。

      public FontTextView(Context context) {
          super(context);
          init();
      }
      
      public FontTextView(Context context, AttributeSet attrs) {
          super(context, attrs);
          init();
      }
      
      public FontTextView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          init();
      }
      
      protected void init() {
          setTypeface(Typeface.createFromAsset(getContext().getAssets(), AppConst.FONT));
      }
      

      【讨论】:

      • 在 4.0 之前的平台上执行此操作时要小心 - 由于 Android 中的错误,这会泄漏大量资源:code.google.com/p/android/issues/detail?id=9904
      • 我们怎样才能恢复到默认值。我正在做这样的事情: if(configShared.getString("storeId", AppCredentials.DEFAULT_STORE_ID).equals("2")){ final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName); staticField.setAccessible(true); staticField.set(null, newTypeface); } else{ 最终字段 staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName); staticField.setAccessible(true); staticField.set(null, null); }
      【解决方案5】:

      是的,有反射。这有效(based on this answer):

      (注意:由于缺乏对自定义字体的支持,这是一种解决方法,所以如果你想改变这种情况,请给android issue here投票)。 注意:不要在这个问题上留下“我也是”的 cmets,当你这样做时,每个盯着它的人都会收到一封电子邮件。所以请给它加星标。

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

      然后您需要重载几个默认字体,例如在 application 类中:

      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

      我调查了cmet中的报告,它不起作用,并且似乎与主题android:Theme.Material.Light不兼容。

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

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

      【讨论】:

      • 如何覆盖等宽粗体或斜体?
      • @ChristopherRivera 有"DEFAULT_BOLD",但也有一个私有字段:private static Typeface[] sDefaults; 您可以尝试通过反射访问它并将sDefaults[Typeface.ITALIC] 设置为yourTypeface。见this.
      • @weston 没关系。我已经实现了。我的理解有误。你已经做了很多工作。你能告诉我一件事吗?为什么它不适用于默认字体?根据您的建议,我已将字体更改为 MONOSPACE,然后应用 loginc,它起作用了。但不适用于 DEFAULT
      • 原生字体不能出错。已修复,通过添加字体路径。 "FontsOverride.setDefaultFont(this, "DEFAULT", "fonts/MyFontAsset.ttf"); "
      • 我发现不得不在任何地方更换默认的TextView 以使用自定义字体或使用这样的基于反射的解决方案真的很烦人。结合 android 中可用的有限字体集,它使开发漂亮的应用程序变得非常困难。我已经向 android 问题跟踪器添加了一个功能请求。如果您想查看此功能,可以在此处为问题加注星标:code.google.com/p/android/issues/…
      【解决方案6】:

      Tom 的解决方案效果很好,但仅适用于 TextView 和 EditText。

      如果你想覆盖大部分视图(RadioGroup、TextView、Checkbox...),我创建了一个方法:

      protected void changeChildrenFont(ViewGroup v, Typeface font){
          for(int i = 0; i < v.getChildCount(); i++){
      
              // For the ViewGroup, we'll have to use recursivity
              if(v.getChildAt(i) instanceof ViewGroup){
                  changeChildrenFont((ViewGroup) v.getChildAt(i), font);
              }
              else{
                  try {
                      Object[] nullArgs = null;
                      //Test wether setTypeface and getTypeface methods exists
                      Method methodTypeFace = v.getChildAt(i).getClass().getMethod("setTypeface", new Class[] {Typeface.class, Integer.TYPE});
                      //With getTypefaca we'll get back the style (Bold, Italic...) set in XML
                      Method methodGetTypeFace = v.getChildAt(i).getClass().getMethod("getTypeface", new Class[] {});
                      Typeface typeFace = ((Typeface)methodGetTypeFace.invoke(v.getChildAt(i), nullArgs));
                      //Invoke the method and apply the new font with the defined style to the view if the method exists (textview,...)
                      methodTypeFace.invoke(v.getChildAt(i), new Object[] {font, typeFace == null ? 0 : typeFace.getStyle()});
                  }
                  //Will catch the view with no such methods (listview...)
                  catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      

      此方法将获取在 XML 中设置的视图样式(粗体、斜体...)并在存在时应用它们。

      对于 ListView,我总是创建一个适配器,并在 getView 中设置字体。

      【讨论】:

        【解决方案7】:

        很简单... 1.下载并将你的自定义字体放入assets..然后为文本视图编写一个单独的类,如下所示:这里我使用了futura字体

        public class CusFntTextView extends TextView {
        
        public CusFntTextView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
        
        public CusFntTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
        
        public CusFntTextView(Context context) {
            super(context);
            init();
        }
        
        private void init() {
            if (!isInEditMode()) {
                Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
                setTypeface(tf);
            }
        }
        

        }

        并在 xml 中执行以下操作:

         <com.packagename.CusFntTextView
                android:id="@+id/tvtitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"         
                android:text="Hi Android"           
                android:textAppearance="?android:attr/textAppearanceLarge"
              />
        

        【讨论】:

        • 是的,这行得通:) 但是,如果我想为其他剩余的视图/小部件应用相同的字体怎么办?我是否也需要为所有其他视图(包括对话框/吐司/操作栏)编写单独的类?
        • 是的,对于这个解决方案,您需要为每个视图编写单独的类。
        【解决方案8】:

        我想改进weston 对 API 21 Android 5.0 的回答。

        原因

        在 API 21 下,大部分文本样式都包含 fontFamily 设置,例如:

        <style name="TextAppearance.Material">
             <item name="fontFamily">@string/font_family_body_1_material</item>
        </style>
        

        应用默认的 Roboto Regular 字体:

        <string name="font_family_body_1_material">sans-serif</string>
        

        原始答案未能应用等宽字体,因为 android:fontFamily 对 android:typeface 属性具有更高的优先级(reference)。 使用 Theme.Holo.* 是一种有效的解决方法,因为里面没有 android:fontFamily 设置。

        解决方案

        由于 Android 5.0 将系统字体放入静态变量 Typeface.sSystemFontMap (reference),我们可以使用相同的反射技术来替换它:

        protected static void replaceFont(String staticTypefaceFieldName,
                final Typeface newTypeface) {
            if (isVersionGreaterOrEqualToLollipop()) {
                Map<String, Typeface> newMap = new HashMap<String, Typeface>();
                newMap.put("sans-serif", newTypeface);
                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 staticField = Typeface.class
                            .getDeclaredField(staticTypefaceFieldName);
                    staticField.setAccessible(true);
                    staticField.set(null, newTypeface);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        

        【讨论】:

        • 对 L 来说很好的解决方法。但是,我想知道为什么这似乎不适用于我的 Buttons 和工具栏标题。我正在覆盖 MainApplication 中的默认字体,如下所示:FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");。我正在使用 Nexus 5,v5.1.1
        • 嘿,谢谢 这很有帮助,只有一件事:这在 Android 6 (API 22) 上不起作用。那么代码是否应该更改为:Build.VERSION.SDK_INT == 21 - 那只是在API 21 我在 Nexus 使用 API 22 进行测试
        • 我仍然无法在 Nexus 9 中使用此方法设置自定义字体
        • 任何有问题的人都不能在 5.1+ 上工作。不要在你的 values-v21 样式中覆盖字体。
        • @MuhammadBabar 谢谢你的解决方案让我很开心
        【解决方案9】:

        为 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>
        

        【讨论】:

          【解决方案10】:

          您可以为每个布局一个一个地设置自定义字体,只需通过传递其根视图从每个布局调用一个函数。首先,创建一个单格方法来访问这样的字体对象

           public class Font {
              private static Font font;
              public Typeface ROBO_LIGHT;
          
              private Font() {
          
              }
          
              public static Font getInstance(Context context) {
                  if (font == null) {
                      font = new Font();
                      font.init(context);
                  }
                  return font;
          
              }
          
              public void init(Context context) {
          
                  ROBO_LIGHT = Typeface.createFromAsset(context.getAssets(),
                          "Roboto-Light.ttf");
              }
          
          }
          

          您可以在上面的类中定义不同的字体,现在定义一个将应用字体的字体助手类:

             public class FontHelper {
          
              private static Font font;
          
              public static void applyFont(View parentView, Context context) {
          
                  font = Font.getInstance(context);
          
                  apply((ViewGroup)parentView);
          
              }
          
              private static void apply(ViewGroup parentView) {
                  for (int i = 0; i < parentView.getChildCount(); i++) {
          
                      View view = parentView.getChildAt(i);
          
          //You can add any view element here on which you want to apply font 
          
                      if (view instanceof EditText) {
          
                          ((EditText) view).setTypeface(font.ROBO_LIGHT);
          
                      }
                      if (view instanceof TextView) {
          
                          ((TextView) view).setTypeface(font.ROBO_LIGHT);
          
                      }
          
                      else if (view instanceof ViewGroup
                              && ((ViewGroup) view).getChildCount() > 0) {
                          apply((ViewGroup) view);
                      }
          
                  }
          
              }
          
          }
          

          在上面的代码中,我只在 textView 和 EditText 上应用字体,你也可以在其他视图元素上应用字体。你只需要将根视图组的 id 传递给上面的应用字体方法。例如你的布局是:

          <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              android:id="@+id/mainParent"
              tools:context="${relativePackage}.${activityClass}" >
          
              <RelativeLayout
                  android:id="@+id/mainContainer"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:layout_above="@+id/homeFooter"
                  android:layout_below="@+id/edit" >
          
                  <ImageView
                      android:id="@+id/PreviewImg"
                      android:layout_width="match_parent"
                      android:layout_height="match_parent"
                      android:src="@drawable/abc_list_longpressed_holo"
                      android:visibility="gone" />
          
                  <RelativeLayout
                      android:id="@+id/visibilityLayer"
                      android:layout_width="match_parent"
                      android:layout_height="fill_parent" >
          
                      <ImageView
                          android:id="@+id/UseCamera"
                          android:layout_width="wrap_content"
                          android:layout_height="wrap_content"
                          android:layout_alignParentTop="true"
                          android:layout_centerHorizontal="true"
                          android:src="@drawable/camera" />
          
                      <TextView
                          android:id="@+id/tvOR"
                          android:layout_width="wrap_content"
                          android:layout_height="wrap_content"
                          android:layout_below="@+id/UseCamera"
                          android:layout_centerHorizontal="true"
                          android:layout_marginTop="20dp"
                          android:text="OR"
                          android:textSize="30dp" />
          
                      <TextView
                          android:id="@+id/tvAND"
                          android:layout_width="wrap_content"
                          android:layout_height="wrap_content"
                          android:layout_centerHorizontal="true"
                          android:layout_marginTop="20dp"
                          android:text="OR"
                          android:textSize="30dp" />
          
          </RelativeLayout>
          

          在上面的布局中,根父 id 是“Main Parent”现在让我们应用字体

          public class MainActivity extends BaseFragmentActivity {
          
              private EditText etName;
              private EditText etPassword;
              private TextView tvTitle;
              public static boolean isHome = false;
          
              @Override
              protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.activity_main);
          
                 Font font=Font.getInstance(getApplicationContext());
                  FontHelper.applyFont(findViewById(R.id.mainParent),          getApplicationContext());
             }    
          }
          

          干杯:)

          【讨论】:

          • 很好.. 不再重新创建相同的字体,所有字段仅使用 1 种字体。 :)
          【解决方案11】:

          我还想改进韦斯顿对 API 21 Android 5.0 的回答。

          我在三星 s5 上使用 DEFAULT 字体时遇到了同样的问题。 (与其他字体一起工作正常)

          我设法通过在 XML 文件中为每个 Textview 或 Button 设置 字体(例如“sans”)使其工作

          <TextView
          android:layout_width="match_parent"
          android:layout_height="39dp"
          android:textColor="@color/abs__background_holo_light"
          android:textSize="12sp"
          android:gravity="bottom|center"
          android:typeface="sans" />
          

          在 MyApplication 类中:

          public class MyApplication extends Application {
              @Override
              public void onCreate() {
              TypefaceUtil.overrideFont(getApplicationContext(), "SANS_SERIF",
              "fonts/my_font.ttf");
              }
          }
          

          希望对你有帮助。

          【讨论】:

            【解决方案12】:

            我写了一个类,为当前视图层次结构中的视图分配字体,并基于当前字体属性(粗体,正常,如果需要,可以添加其他样式):

            public final class TypefaceAssigner {
            
            public final Typeface DEFAULT;
            public final Typeface DEFAULT_BOLD;
            
            @Inject
            public TypefaceAssigner(AssetManager assetManager) {
                DEFAULT = Typeface.createFromAsset(assetManager, "TradeGothicLTCom.ttf");
                DEFAULT_BOLD = Typeface.createFromAsset(assetManager, "TradeGothicLTCom-Bd2.ttf");
            }
            
            public void assignTypeface(View v) {
                if (v instanceof ViewGroup) {
                    for (int i = 0; i < ((ViewGroup) v).getChildCount(); i++) {
                        View view = ((ViewGroup) v).getChildAt(i);
                        if (view instanceof ViewGroup) {
                            setTypeface(view);
                        } else {
                            setTypeface(view);
                        }
                    }
                } else {
                    setTypeface(v);
                }
            }
            
            private void setTypeface(View view) {
                if (view instanceof TextView) {
                    TextView textView = (TextView) view;
                    Typeface typeface = textView.getTypeface();
                    if (typeface != null && typeface.isBold()) {
                        textView.setTypeface(DEFAULT_BOLD);
                    } else {
                        textView.setTypeface(DEFAULT);
                    }
                }
            }
            }
            

            现在在 onViewCreated 或 onCreateView 中的所有片段、onCreate 中的所有活动以及 getView 或 newView 中的所有视图适配器中调用:

            typefaceAssigner.assignTypeface(view);
            

            【讨论】:

              【解决方案13】:

              我想改进 westonRoger Huang 对 API 21 主题为“Theme.AppCompat”的 Android 棒棒糖的回答。

              Android 4.4 以下

              <resources>
                  <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
                  </style>
              
                 <!-- Application theme. -->
                 <style name="AppTheme" parent="AppBaseTheme">
                     <item name="android:typeface">monospace</item>
                 </style>
              </resources>
              

              超过(等于)API 5.0

              <resources>
                  <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
                  </style>
              
                 <!-- Application theme. -->
                 <style name="AppTheme" parent="AppBaseTheme">
                     <item name="android:textAppearance">@style/CustomTextAppearance</item>
                 </style>
              
                 <style name="CustomTextAppearance">
                     <item name="android:typeface">monospace</item>
                 </style>
              </resources>
              

              FontsOverride 实用程序文件与weston 的答案相同。 我已经在这些手机上进行了测试:

              Nexus 5(android 5.1 Primary Android System)

              中兴V5(android 5.1 CM12.1)

              小米笔记(android 4.4 MIUI6)

              HUAWEI C8850(android 2.3.5 UNKNOWN)

              【讨论】:

                【解决方案14】:

                可以在此处找到出色的解决方案:https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere

                只需从 BaseActivity 扩展活动并编写这些方法。此外,您应该更好地缓存字体,如下所述:https://stackoverflow.com/a/16902532/2914140


                经过一些研究,我编写了适用于三星 Galaxy Tab A (Android 5.0) 的代码。使用weston和Roger Huang的代码以及https://stackoverflow.com/a/33236102/2914140。还在联想 TAB 2 A10-70L 上进行了测试,但无法正常工作。 我在这里插入了一个字体“Comic Sans”来看看有什么不同。

                import android.content.Context;
                import android.graphics.Typeface;
                import android.os.Build;
                import android.util.Log;
                import java.lang.reflect.Field;
                import java.util.HashMap;
                import java.util.Map;
                
                public class FontsOverride {
                    private static final int BOLD = 1;
                    private static final int BOLD_ITALIC = 2;
                    private static final int ITALIC = 3;
                    private static final int LIGHT = 4;
                    private static final int CONDENSED = 5;
                    private static final int THIN = 6;
                    private static final int MEDIUM = 7;
                    private static final int REGULAR = 8;
                
                    private Context context;
                
                    public FontsOverride(Context context) {
                        this.context = context;
                    }
                
                    public void loadFonts() {
                        Map<String, Typeface> fontsMap = new HashMap<>();
                        fontsMap.put("sans-serif", getTypeface("comic.ttf", REGULAR));
                        fontsMap.put("sans-serif-bold", getTypeface("comic.ttf", BOLD));
                        fontsMap.put("sans-serif-italic", getTypeface("comic.ttf", ITALIC));
                        fontsMap.put("sans-serif-light", getTypeface("comic.ttf", LIGHT));
                        fontsMap.put("sans-serif-condensed", getTypeface("comic.ttf", CONDENSED));
                        fontsMap.put("sans-serif-thin", getTypeface("comic.ttf", THIN));
                        fontsMap.put("sans-serif-medium", getTypeface("comic.ttf", MEDIUM));
                        overrideFonts(fontsMap);
                    }
                
                    private void overrideFonts(Map<String, Typeface> typefaces) {
                        if (Build.VERSION.SDK_INT == 21) {
                            try {
                                final Field field = Typeface.class.getDeclaredField("sSystemFontMap");
                                field.setAccessible(true);
                                Map<String, Typeface> oldFonts = (Map<String, Typeface>) field.get(null);
                                if (oldFonts != null) {
                                    oldFonts.putAll(typefaces);
                                } else {
                                    oldFonts = typefaces;
                                }
                                field.set(null, oldFonts);
                                field.setAccessible(false);
                            } catch (Exception e) {
                                Log.e("TypefaceUtil", "Cannot set custom fonts");
                            }
                        } else {
                            try {
                                for (Map.Entry<String, Typeface> entry : typefaces.entrySet()) {
                                    final Field staticField = Typeface.class.getDeclaredField(entry.getKey());
                                    staticField.setAccessible(true);
                                    staticField.set(null, entry.getValue());
                                }
                            } catch (NoSuchFieldException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                
                    private Typeface getTypeface(String fontFileName, int fontType) {
                        final Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/" + fontFileName);
                        return Typeface.create(tf, fontType);
                    }
                }
                

                要在整个应用程序中运行代码,您应该在 Application 类中编写以下代码:

                    new FontsOverride(this).loadFonts();
                

                在 'assets' 中创建一个文件夹 'fonts' 并将所需的字体放在那里。一个简单的指令可以在这里找到:https://stackoverflow.com/a/31697103/2914140

                Lenovo 设备也错误地获取了字体值。在大多数情况下,它返回 Typeface.NORMAL,有时返回 null。即使 TextView 是粗体的(在 xml 文件布局中)。见这里:TextView isBold always returns NORMAL。这样,屏幕上的文本总是使用常规字体,而不是粗体或斜体。所以我认为这是制作人的错误。

                【讨论】:

                • 我研究了android源代码,你的答案是最好的。它可以代替薄、中、轻或其他任何东西。非常感谢!
                【解决方案15】:

                android中有一个很棒的自定义字体库: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()
                        );
                    }
                } 
                

                并在活动类中将此方法放在onCreate之前:

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

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

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

                它会将整个活动更改为您的字体!它简单干净!

                【讨论】:

                • 这是完美的解决方案。
                • 确实,它非常完美的解决方案,这样我就不必编写不同的类来更新菜单项、单选按钮或复选框字体的字体。它适用于所有人!谢谢:)
                • 绝对不是一个完美的解决方案:粗体/斜体/粗斜体样式被覆盖。目前没有简单的方法来设置这些族。
                • 如果我这样做了如何从粗体切换到常规?
                • 为了获得粗体、斜体或下划线功能,我在strings.xml 文件中使用了&lt;b&gt;&lt;/b&gt;&lt;u&gt;&lt;/u&gt;&lt;i&gt;&lt;/i&gt;,到目前为止它可以工作。
                【解决方案16】:

                总结:

                选项#1:使用反射来应用字体(结合westonRoger Huang的答案):

                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) {
                        if (isVersionGreaterOrEqualToLollipop()) {
                            Map<String, Typeface> newMap = new HashMap<String, Typeface>();
                            newMap.put("sans-serif", newTypeface);
                            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 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");
                    } 
                } 
                

                设置一种样式以强制该字体字体应用范围(基于lovefish):

                前棒棒糖:

                <resources>
                    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
                    </style>
                
                   <!-- Application theme. -->
                   <style name="AppTheme" parent="AppBaseTheme">
                       <item name="android:typeface">monospace</item>
                   </style>
                </resources>
                

                棒棒糖(API 21):

                <resources>
                    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
                    </style>
                
                   <!-- Application theme. -->
                   <style name="AppTheme" parent="AppBaseTheme">
                       <item name="android:textAppearance">@style/CustomTextAppearance</item>
                   </style>
                
                   <style name="CustomTextAppearance">
                       <item name="android:typeface">monospace</item>
                   </style>
                </resources>
                

                选项2:子类化您需要自定义字体的每个视图,即。 ListView、EditTextView、Button等(Palani的回答):

                public class CustomFontView extends TextView {
                
                public CustomFontView(Context context, AttributeSet attrs, int defStyle) {
                    super(context, attrs, defStyle);
                    init(); 
                } 
                
                public CustomFontView(Context context, AttributeSet attrs) {
                    super(context, attrs);
                    init(); 
                } 
                
                public CustomFontView(Context context) {
                    super(context);
                    init(); 
                } 
                
                private void init() { 
                    if (!isInEditMode()) {
                        Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
                        setTypeface(tf);
                    } 
                } 
                

                选项 3: 实现一个遍历当前屏幕的视图层次结构的视图爬虫:

                变体#1(Tom 的回答):

                public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
                { 
                    if (mContainer == null || mFont == null) return;
                
                    final int mCount = mContainer.getChildCount();
                
                    // Loop through all of the children. 
                    for (int i = 0; i < mCount; ++i)
                    { 
                        final View mChild = mContainer.getChildAt(i);
                        if (mChild instanceof TextView)
                        { 
                            // Set the font if it is a TextView. 
                            ((TextView) mChild).setTypeface(mFont);
                        } 
                        else if (mChild instanceof ViewGroup)
                        { 
                            // Recursively attempt another ViewGroup. 
                            setAppFont((ViewGroup) mChild, mFont);
                        } 
                        else if (reflect)
                        { 
                            try { 
                                Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
                                mSetTypeface.invoke(mChild, mFont); 
                            } catch (Exception e) { /* Do something... */ }
                        } 
                    } 
                } 
                

                用法:

                final ViewGroup mContainer = (ViewGroup) findViewById(
                android.R.id.content).getRootView();
                HomeActivity.setAppFont(mContainer, Typeface.createFromAsset(getAssets(),
                "fonts/MyFont.ttf"));
                

                变体#2:https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere

                选项 #4: 使用名为 Calligraphy 的第 3 方库。

                就个人而言,我会推荐选项#4,因为它可以省去很多麻烦。

                【讨论】:

                • Option#1 中,newMap.put( 中的"sans-serif"styles 中的monospace 是什么意思?!我想使用一个名为 barana.ttf 的自定义字体。
                • 在选项 1 中,第一个代码 sn-p 不适用于 API 级别 22 ,Android 5.1.1 。但是其他代码部分可以。如果条件如此,确切的情况应该是什么?
                【解决方案17】:

                This 解决方案在某些情况下无法正常工作。
                所以我扩展它:

                FontsReplacer.java

                public class MyApplication extends Application {
                
                    @Override
                    public void onCreate() {
                        FontsReplacer.replaceFonts(this);
                        super.onCreate();
                    }
                
                }
                

                https://gist.github.com/orwir/6df839e3527647adc2d56bfadfaad805

                【讨论】:

                  【解决方案18】:

                  Calligraphy 效果很好,但它不适合我,因为它不支持字体系列的不同权重(粗体、斜体等)。

                  所以我尝试了Fontain,它允许您定义自定义视图并应用它们自定义字体系列。

                  为了使用 Fontain,您应该将以下内容添加到您的应用模块 build.gradle:

                  compile 'com.scopely:fontain:1.0.0'
                  

                  那么,你应该使用 FontTextView,而不是使用常规的 TextView

                  带有大写和粗体内容的 FontTextView 示例:

                   <com.scopely.fontain.views.FontTextView
                              android:layout_width="match_parent"
                              android:layout_height="wrap_content"
                              android:background="@android:color/black"
                              android:textColor="@android:color/white"
                              android:textSize="11dp"
                              android:gravity="center"
                              android:id="@+id/tv1"
                              app:font_family="myCustomFont"
                              app:caps_mode="characters"
                              app:font_weight="BOLD"/>
                  

                  【讨论】:

                    【解决方案19】:
                    package com.theeasylearn.demo.designdemo;
                    import android.content.Context;
                    import android.graphics.Typeface;
                    import android.util.AttributeSet;
                    import android.widget.TextView;
                    
                    public class MyButton extends TextView {
                    
                        public MyButton(Context context, AttributeSet attrs, int defStyle) {
                            super(context, attrs, defStyle);
                            init();
                        }
                    
                        public MyButton(Context context, AttributeSet attrs) {
                            super(context, attrs);
                            init();
                        }
                    
                        public MyButton(Context context) {
                            super(context);
                            init();
                        }
                    
                        private void init() {
                    
                                Typeface tf =
                                        Typeface.createFromAsset(
                                                getContext().getAssets(), "angelina.TTF");
                                setTypeface(tf);
                    
                        }
                    
                    }
                    

                    【讨论】:

                    • 不是一个完美的解决方案。使用此解决方案将需要自定义解决方法来访问默认 Android TextViews
                    【解决方案20】:

                    从 Android O 开始,现在可以直接从 XML 和 my bug is now closed! 进行定义

                    See here for details

                    TL;DR:

                    首先你必须将你的字体添加到项目中

                    第二次添加字体系列,如下所示:

                    <?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>
                    

                    最后,您可以在布局或样式中使用字体:

                    <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:fontFamily="@font/lobster"/>
                    
                    <style name="customfontstyle" parent="@android:style/TextAppearance.Small">
                        <item name="android:fontFamily">@font/lobster</item>
                    </style>
                    

                    享受吧!

                    【讨论】:

                    • 谢谢。我们应该使用 Android Studio 2.4 来添加字体文件夹。
                    • 其实我只需要将它应用到我的风格中,它就会通过应用程序应用。但有些(需要通过java代码应用程序添加的控件)
                    【解决方案21】:

                    在 api 26 和 build.gradle 3.0.0 及更高版本中,您可以在 res 中创建字体目录 并以你的风格使用这条线

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

                    为了改变 build.gradle 在你的 build.gradle 依赖项中使用它

                    classpath 'com.android.tools.build:gradle:3.0.0'
                    

                    【讨论】:

                    • 以及如何在项目中插入字体?
                    • @azerafati 将你的字体复制到 res/font
                    • 是的,tanx。我已经明白了。但是 item 本身并不能为整个应用程序设置字体。有什么线索吗?
                    【解决方案22】:

                    最终,Google 意识到了这个问题的严重性(将自定义字体应用到 UI 组件),他们为此设计了一个干净的解决方案。

                    首先,您需要更新以支持库 26+(您可能还需要更新您的 gradle{4.0+},android studio),然后您可以创建一个名为 font 的新资源文件夹。在此文件夹中,您可以放置​​您的字体资源(.tff,...)。 然后你需要覆盖默认应用程序并强制你的自定义字体进入它:)

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

                    注意:如果你想支持 API 早于 16 的设备,你必须使用 app 命名空间而不是 android!

                    【讨论】:

                      【解决方案23】:

                      要更改 TextView 的默认字体系列,请在您的应用主题中覆盖 textViewStyle。

                      要在 fontFamily 中使用自定义字体,请使用支持库中的字体资源。

                      该功能是在 Android 26 中添加的,但通过 supportlib 向后移植到旧版本。

                      https://developer.android.com/guide/topics/resources/font-resource.html https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html#using-support-lib

                      【讨论】:

                        【解决方案24】:

                        自从 Android Oreo 及其支持库 (26.0.0) 发布以来,您可以轻松地做到这一点。在另一个问题中参考this answer

                        基本上你的最终风格应该是这样的:

                        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
                           <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
                        </style>
                        

                        【讨论】:

                          【解决方案25】:

                          我发现calligraphy 3 librarycoderwall's post 的混合是我的最终结果。

                          【讨论】:

                            猜你喜欢
                            • 2011-11-28
                            • 2012-08-30
                            • 2016-02-28
                            • 1970-01-01
                            • 2016-02-05
                            • 2011-05-22
                            • 1970-01-01
                            • 2016-04-11
                            相关资源
                            最近更新 更多