在Android中使用自定义字体

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

所以,

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

有没有办法从XML做到这一点?

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

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

不是我所知道的。

是的这是可能的。

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

values文件夹中的attrs.xml中:

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

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

我以更加“蛮力”的方式做到了这一点,不需要修改layout xml或者Activities。

testingAndroid 2.1至4.4版本。 在应用程序启动时,在您的应用程序类中运行:

 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

尽pipe我将Manish的答案作为最快最有针对性的方法,但我也看到了一些简单的解决scheme,它们recursion地遍历视图层次结构并依次更新所有元素的字体。 像这样的东西:

 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()方法之后,在你的视图上调用这个函数。

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

在这里输入图像描述

我有以下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")); } } 

我还没Fragment s来testing这个代码,但希望它能工作。

我的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; } } 

嘿,我也需要2个不同的字体在我的应用程序不同的widgeds! 我用这种方式:

在我的应用程序类我创build一个静态方法:

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

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

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

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

使用pospi的build议,并使用像理查德这样的“标签”属性,我创build了一个自定义类,加载我的自定义字体,并根据他们的标签将它们应用到视图。

所以基本上,而不是在android:fontFamily属性中设置TypeFace,而是使用android:标签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); 

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

 /** * 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; } } } 

我在Lisa Wray的博客上find了一个很好的解决scheme。 使用新的数据绑定,可以在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_width="wrap_content" android:layout_height="wrap_content"/> 

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

本文提供了这些机制的更深入的解释,以及作者如何尝试以这种方式为字体提供xml布局支持。 作者的实现代码可以在这里find。

将自定义字体设置为常规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); 

是的,通过覆盖默认字体是可能的。 我遵循这个解决scheme,对于所有的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> 

在上面的链接中提到的themes.xml,我提到了在我的默认应用主题标签的styles.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如何处理字体和字体以及它们的默认值,这是一个不同的课程点。

我不知道它是否改变了整个应用程序,但我已经设法改变了一些不能改变的组件:

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

我喜欢pospi的build议。 为什么不全面使用视图的“标签”属性(您可以在XML中指定 – “android:tag”)来指定在XML中无法执行的任何其他样式。 我喜欢JSON,所以我会使用JSONstring来指定一个键/值集。 这个类可以完成这个工作,只需在你的activity中调用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的字体。

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

有一件事你会在代码中看到 – 有些视图有一个标签没有明确设置 – 奇怪的是string'Αποκοπή' – 根据谷歌翻译'在希腊'切'! 我勒个去…?

@ majinboo的答案是修改性能和内存pipe理。 任何多于一种字体需要相关的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 } } } 

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

它看起来像使用自定义字体已经很容易与Android O,你可以基本上使用XML来实现这一点。 我附上了Android官方文档的链接供参考,希望这可以帮助那些仍然需要这个解决scheme的人。 在Android中使用自定义字体

新的支持库26现在允许您使用XML中的字体。

这是如何在XML中的字体

绝对有可能。 许多方法来做到这一点。 最快的方法,用try-catch方法创build条件。尝试一定的字体样式条件,捕捉错误,并定义其他字体样式。