Uso de una tipografía personalizada en Android

Quiero utilizar una fuente personalizada para mi aplicación Android que estoy creando.
Puedo cambiar individualmente la tipografía de cada objeto del código, pero tengo cientos de ellos.

Asi que,

  • ¿Hay una manera de hacer esto del XML? [Establecimiento de una tipografía personalizada]
  • ¿Hay una manera de hacerlo desde el código en un lugar, para decir que toda la aplicación y todos los componentes deben utilizar la tipografía personalizada en lugar de la predeterminada?

¿Hay una manera de hacer esto del XML?

No, lo siento. Sólo puede especificar los tipos de letra incorporados a través de XML.

¿Hay una manera de hacerlo desde el código en un lugar, para decir que toda la aplicación y todos los componentes deben utilizar la tipografía personalizada en lugar de la predeterminada?

No que yo sepa.

Sí, es posible.

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

En attrs.xml en la carpeta de values :

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

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

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 Android versión 2.1 a 4.4. Ejecutar esto en el 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

Aunque estoy mejorando la respuesta de Manish como el método más rápido y más orientado, también he visto soluciones ingenuas que recurren recursivamente a través de una jerarquía de vistas y actualizan los tipos de letra de todos los elementos a su vez. 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 } } 

Tendrás que llamar a esta función en tus vistas tanto después de inflar el diseño como en los métodos onContentChanged() tu actividad.

He podido hacer esto de una manera centralizada, aquí está el resultado:

Introduzca aquí la descripción de la imagen

Tengo Activity siguiente y extiendo de él si necesito fuentes de encargo:

 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 utilizando 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 , pero espero que funcione.

Mi FontUtils es sencillo, que también soluciona el problema anterior al ICS mencionado 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<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; } } 

¡Hey yo también necesito 2 diversas fuentes en mi app para widgeds diferentes! 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. (He creado una clase Constants) Ahora puedes usar esto en todas partes en tu aplicación:

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

El único problema es que necesitas esto para cada widget donde quieras usar la Fuente! Pero creo que esta es la mejor manera.

Utilizando la sugerencia de pospi y trabajando con la propiedad 'tag' como lo hizo Richard , he creado una clase personalizada que carga mis fuentes personalizadas y las aplica a las vistas según sus etiquetas.

Así que básicamente, en lugar de configurar el TypeFace en el atributo android: fontFamily está utilizando el android: etiqueta attritube y establecerlo en uno de los enums definidos.

 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 su Actividad o Fragmento, llame

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

Creo que puede haber una manera más práctica de hacerlo. La clase siguiente establecerá una cara de tipo personalizada para todos los componentes de su 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/<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; } } } 

He encontrado una buena solución en el blog de Lisa Wray . Con el nuevo enlace de datos es posible establecer 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:

 <TextView app:font="@{`Source-Sans-Pro-Regular.ttf`}" android:layout_width="wrap_content" android:layout_height="wrap_content"/> 

Las implementaciones predeterminadas de LayoutInflater no admiten la especificación del tipo de letra de xml. Sin embargo, he visto que se hace en xml, proporcionando una fábrica personalizada para el LayoutInflater que analizará tales atributos de la etiqueta xml.

A 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 detallada de estos mecanismos y cómo el autor intenta proporcionar compatibilidad 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); 

No sé si cambia la aplicación en su totalidad, pero he logrado cambiar algunos componentes que de otro modo no se podrían cambiar haciendo 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 todo el uso de la propiedad 'tag' de una vista (que puede especificar en XML – 'android: tag') para especificar cualquier estilo adicional que no pueda hacer en XML. Me gusta JSON por lo que utilizaría una cadena JSON para especificar un conjunto de clave / valor. Esta clase hace el trabajo – solo 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, usted querrá manejar más que sólo el tipo de letra para hacer que valga la pena usar JSON.

Un beneficio de la propiedad 'etiqueta' es que se puede establecer en un estilo de base que se utiliza como un tema y, por tanto, que se apliquen a todas sus vistas de forma automática. EDIT: Hacer esto resulta en un accidente durante la inflación en Android 4.0.3. Todavía puede utilizar un estilo y aplicarlo a las vistas de texto de forma individual.

Una cosa que verás en el código – algunas vistas tienen una etiqueta sin que se establezca explícitamente – extrañamente es la cadena 'Αποκοπή' – que es 'cortar' en griego, de acuerdo con google translate! Que demonios…?

@ Majinboo respuesta se revisa para el rendimiento y gestión de la memoria. Cualquier actividad de necesidad de más de una fuente relacionada puede utilizar esta clase de fuente dando el propio constructor como un parámetro.

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

La clase de fuentes revisada es la siguiente:

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

Trabajo 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:

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

Parece que el uso de fuentes personalizadas se ha hecho fácil con Android O, básicamente puede utilizar xml para lograr esto. He adjuntado un enlace a la documentación oficial de Android para referencia, y espero que esto ayude a las personas que todavía necesitan esta solución. Trabajo con fuentes personalizadas en Android

Sí, es posible reemplazando el tipo de letra predeterminado. He seguido esta solución y funcionó como un encanto para todos los TextViews y el texto de ActionBar demasiado con un solo cambio.

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

Inicialmente, no sabía que los tipos de letra a sobrescribir son fijos y el conjunto de valores definidos, pero finalmente me ayudó a entender cómo Android trata con fuentes y tipos de letra y sus valores por defecto, que es un punto diferente ofcourse.

La nueva biblioteca de soporte 26 le permite ahora utilizar fuentes en XML.

Aquí es cómo hacerlo Fuentes en XML

Absolutamente posible. Hay muchas maneras de hacerlo. La forma más rápida, crear condiciones con el método try – catch. Pruebe su condición de estilo de fuente determinada, captura el error y defina el otro estilo de fuente.

  • Mapa de bits de StateListDrawable y mosaico
  • Definición de archivo xml para el botón para cambiar el fondo Y textColor basado en el estado
  • ¿Cómo puedo mostrar dos imágenes lado a lado en Android?
  • AndroidManifest.xml - especificado para la propiedad 'manifiesto' no existe
  • El componente personalizado de Android funciona bien en tiempo de ejecución, pero no funciona en tiempo de diseño
  • Cómo obtener valores de BuildConfig desde xml?
  • Error de vinculación de datos de Android al utilizar etiqueta de inclusión con diseño de vista personalizada
  • Visualización de la variable en pantalla usando Android por TextViews
  • Quitar sombra de Autodesplegable de AutoCompleteTextView
  • Cambiar (Pulgar y Seguir) Android
  • Android NumberPicker: Establecer min, max, por defecto de XML
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.