Usar una tipografía personalizada en Android

Quiero usar una fuente personalizada para mi aplicación de Android que estoy creando.
Puedo cambiar individualmente el tipo de letra de cada objeto de Code, pero tengo cientos de ellos.

Asi que,

  • ¿Hay alguna manera de hacer esto desde XML? [Establecer una tipografía personalizada]
  • ¿Hay alguna manera de hacerlo desde el código en un solo lugar, decir que toda la aplicación y todos los componentes deberían usar el tipo de letra personalizado en lugar del predeterminado?

¿Hay alguna manera de hacer esto desde XML?

No lo siento. Solo puede especificar los tipos de letra integrados a través de XML.

¿Hay alguna manera de hacerlo desde el código en un solo lugar, decir que toda la aplicación y todos los componentes deberían usar el tipo de letra personalizado en lugar del predeterminado?

No que yo sepa.

Hay una variedad de opciones para estos hoy en día:

  • Recursos de fuente y backports en el Android SDK, si está utilizando appcompat

  • Las bibliotecas de terceros para quienes no usan appcompat , aunque no todas, admitirán la definición de la fuente en los recursos de diseño.

Sí, es posible.

Debe crear una vista personalizada que amplíe la vista de texto.

En attrs.xml en la carpeta de values :

        

En main.xml :

     

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

Hice esto de una manera más “fuerza bruta” que no requiere cambios en el diseño xml o Actividades.

Probado en la versión de Android 2.1 a 4.4. Ejecute esto al inicio de la aplicación, en su clase de aplicación:

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

Para obtener un ejemplo más completo, consulte http://github.com/perchrh/FontOverrideExample

A pesar de que estoy votando la respuesta de Manish como el método más rápido y más específico, también he visto soluciones ingenuas que simplemente iteran recursivamente a través de una jerarquía de vistas y actualizan todos los tipos de letra de los elementos. Algo como esto:

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

onContentChanged() llamar a esta función en sus vistas después de inflar el diseño y en los métodos onContentChanged() su actividad.

Pude hacer esto de forma centralizada, aquí está el resultado:

enter image description here

Tengo la siguiente Activity y la extiendo si necesito fonts personalizadas:

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

Pero si estoy usando una actividad del paquete de soporte, por ejemplo, FragmentActivity entonces uso esta 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")); } } 

Todavía no he probado este código con Fragment s, pero espero que funcione.

My FontUtils es simple, que también resuelve el problema anterior al ICS que se menciona aquí 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 TYPEFACE = new HashMap(); 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; } } 

¡Hola, también necesito 2 fonts diferentes en mi aplicación para diferentes dimensiones! Yo uso de esta manera:

En mi clase de aplicación, creo un método estático:

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

El tipo de letra String representa el xyz.ttf en la carpeta de activos. (Creé una clase Constants) Ahora puede usar esto en todas partes en su aplicación:

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

El único problema es que lo necesitas para cada widget en el que quieras usar la fuente. Pero creo que esta es la mejor manera.

Utilizando la sugerencia de pospi y trabajando con la propiedad ‘etiqueta’ como lo hizo Richard , creé una clase personalizada que carga mis fonts personalizadas y las aplica a las vistas de acuerdo con sus tags.

Así que, básicamente, en lugar de establecer TypeFace en el atributo android: fontFamily, está utilizando android: tag attritube y establecerlo en una de las enumeraciones definidas.

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

En tu Actividad o Fragmento simplemente llamas

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

Creo que puede haber una forma más práctica de hacerlo. La siguiente clase configurará un tipo de letra personalizado para todos sus componentes de la aplicación (con una configuración por clase).

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

Encontré una buena solución en el blog de Lisa Wray . Con el nuevo enlace de datos, es posible configurar la fuente en sus archivos XML.

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

En XML:

  

Las implementaciones predeterminadas de LayoutInflater no son compatibles con especificar el tipo de fuente de xml. Sin embargo, lo he visto en xml al proporcionar una fábrica personalizada para el LayoutInflater que analizará dichos atributos desde la etiqueta xml.

La estructura básica le gustaría esto.

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

Este artículo proporciona una explicación más profunda de estos mecanismos y cómo el autor intenta proporcionar soporte de diseño xml para tipos de letra de esta manera. El código para la implementación del autor se puede encontrar aquí .

Establecer una fuente personalizada en un ProgressDialog / AlertDialog regular:

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

Sí, es posible anulando el tipo de letra predeterminado. Seguí esta solución y funcionó como un amuleto para todos los textos de TextViews y ActionBar con un solo cambio.

 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

   

En lugar de themes.xml como se menciona en el enlace anterior, mencioné la fuente predeterminada para sobrescribir en mi styles.xml en mi etiqueta de tema predeterminada de la aplicación. Los tipos de letra predeterminados que se pueden sobrescribir son serif, sans, monoespacio y normal.

Tipo de letraUtil.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); } } } 

Inicialmente, no sabía que los tipos de letra que se sobrescriben son fijos y el conjunto de valores definidos, pero finalmente me ayudó a entender cómo trata Android con las fonts y tipos de letra y sus valores predeterminados, que es un punto diferente, por supuesto.

No sé si cambia toda la aplicación, pero he logrado cambiar algunos componentes que de otra manera no podrían cambiarse al hacer esto:

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

Me gusta la sugerencia de pospi. ¿Por qué no utilizar todas las funciones de la etiqueta ‘tag’ de una vista (que puede especificar en XML – ‘android: tag’) para especificar cualquier estilo adicional que no se puede hacer en XML? Me gusta JSON, así que usaría una cadena JSON para especificar un conjunto clave / valor. Esta clase hace el trabajo: simplemente llame a Style.setContentView(this, [resource id]) en su actividad.

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

Obviamente, querrás manejar algo más que el tipo de letra para que el uso de JSON valga la pena.

Un beneficio de la propiedad 'etiqueta' es que puede establecerlo en un estilo base que use como tema y así aplicarlo a todas sus vistas automáticamente. EDITAR: Al hacerlo, se produce un locking durante la inflación en Android 4.0.3. Aún puede usar un estilo y aplicarlo a las vistas de texto individualmente.

Una cosa que verás en el código - algunas vistas tienen una etiqueta sin una definida explícitamente - extrañamente es la cadena 'Αποκοπή' - que es 'cortada' en griego, de acuerdo con Google translate. Que demonios...?

La respuesta de @ majinboo se revisa para la gestión del rendimiento y la memoria. Más de una fuente necesita actividad relacionada. Puede usar esta clase de Fuente dándole al constructor como un parámetro.

 @Override public void onCreate(Bundle savedInstanceState) { Font font = new Font(this); } 

La clase Revised Fonts es la siguiente:

 public class Fonts { private HashMap hashMapFonts; private enum AssetTypefaces { RobotoLight, RobotoThin, RobotoCondensedBold, RobotoCondensedLight, RobotoCondensedRegular } public Fonts(Context context) { AssetManager mngr = context.getAssets(); hashMapFonts = new HashMap(); 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 } } } 

Trabajando para Xamarin.Android:

Clase:

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

Implementación de la aplicación:

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

Estilo:

  

Parece que usar fonts personalizadas se ha simplificado con Android O, básicamente puedes usar xml para lograrlo. He adjuntado un enlace a la documentación oficial de Android como referencia, y espero que esto ayude a las personas que aún necesitan esta solución. Trabajando con fonts personalizadas en Android

La nueva biblioteca de soporte 26 ahora le permite usar fonts en XML.

Aquí está cómo hacerlo Fuentes en XML

Absolutamente posible. Muchas formas de hacerlo. La forma más rápida, crea una condición con el método try – catch … prueba tu condición de cierto estilo de fuente, detecta el error y define el otro estilo de fuente.