¿Es posible establecer una fuente personalizada para toda la aplicación?


270

Necesito usar cierta fuente para toda mi aplicación. Tengo un archivo .ttf para lo mismo. ¿Es posible establecer esto como fuente predeterminada, al inicio de la aplicación y luego usarla en otra parte de la aplicación? Cuando lo configuro, ¿cómo lo uso en mis XML de diseño?


en android studio 3.0 puede configurar fácilmente fuentes personalizadas: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher

@MHSFisher Fonts en XML es una función para Android 8.0 (API nivel 26)
Emi Raz

1
@EmiRaz, lea el documento developer.android.com/guide/topics/ui/look-and-feel/… . esta característica se agregó en la biblioteca de soporte 26, pero es compatible con API 16.
MHSFisher

Respuestas:


450

Si con reflexion. Esto funciona ( basado en esta respuesta ):

(Nota: esta es una solución alternativa debido a la falta de soporte para las fuentes personalizadas, por lo que si desea cambiar esta situación, por favor, marque el tema de Android aquí ). Nota: No deje comentarios "yo también" sobre ese tema, todos los que lo han visto reciben un correo electrónico cuando lo hace. Así que solo "estrella" por favor.

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

Luego debe sobrecargar las pocas fuentes predeterminadas, por ejemplo en una clase de aplicación :

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

O, por supuesto, si está utilizando el mismo archivo de fuente, puede mejorarlo para cargarlo solo una vez.

Sin embargo, tiendo a anular uno, por ejemplo "MONOSPACE", luego configurar un estilo para forzar la aplicación de tipo de letra en toda la extensión:

<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

He investigado los informes en los comentarios de que no funciona y parece ser incompatible con el tema android:Theme.Material.Light.

Si ese tema no es importante para usted, use un tema anterior, por ejemplo:

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

11
¿Cómo anularía el monoespacio en negrita o cursiva?
Christopher Rivera

2
@ChristopherRivera Existe "DEFAULT_BOLD"pero también un campo privado: private static Typeface[] sDefaults;puede intentar acceder a esto reflexionando y configurando sDefaults[Typeface.ITALIC]su Tipo de letra. Mira esto.
Weston

55
@weston No importa. Lo he logrado Hubo un error en mi comprensión. Has hecho un gran trabajo. ¿Puedes decirme una cosa? ¿Por qué no funciona para el tipo de letra PREDETERMINADO? Cambié Typeface a MONOSPACE como su sugerencia y luego apliqué el loginc, funcionó. Pero no funciona para DEFAULT
Jay Pandya

2
El tipo de letra nativo no puede ser un error. Solucionado, agregando la ruta de las fuentes. "FontsOverride.setDefaultFont (this," DEFAULT "," fonts / MyFontAsset.ttf ");"
Sai

3
Me resulta realmente molesto tener que cambiar el valor predeterminado en TextViewtodas partes para usar fuentes personalizadas o usar una solución basada en la reflexión como esta. Combinado con el conjunto limitante de fuentes disponibles en Android, hace que el desarrollo de aplicaciones atractivas sea mucho trabajo duro. He agregado una solicitud de función al rastreador de problemas de Android. Si desea ver esta función, puede marcar
Sam

64

Hay una gran biblioteca para fuentes personalizadas en Android: caligrafía

Aquí hay una muestra de cómo usarlo.

en Gradle necesita poner esta línea en el archivo build.gradle de su aplicación:

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

y luego crea una clase que se extienda Applicationy escribe este código:

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

y en la clase de actividad ponga este método antes de onCreate:

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

y lo último que debería tener tu archivo de manifiesto es este:

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

¡y cambiará toda la actividad a tu fuente! ¡Es simple y limpio!


1
Esta es la solución perfecta.
nizam.sp

1
Es cierto que es la solución perfecta, de esta manera no tengo que escribir diferentes clases para actualizar las fuentes de los elementos del menú, botón de opción o casilla de verificación fotns. Se aplica para todos !! gracias :)
Manisha

2
Definitivamente no es una solución perfecta: los estilos en negrita / cursiva / negrita-cursiva se sobrescriben. Actualmente no hay una manera fácil de establecer esas familias.
0101100101

2
Si hago eso, ¿cómo puedo cambiar de negrita a regular?
GMX

2
Para obtener la función de negrita, cursiva o subrayado, he usado <b></b>, <u></u>y <i></i>en el strings.xmlarchivo y hasta ahora funciona.
Jlively

47

Si bien esto no funcionaría para una aplicación completa, funcionaría para una Actividad y podría reutilizarse para cualquier otra Actividad. He actualizado mi código gracias a @ FR073N para admitir otras vistas. No estoy seguro de los problemas con Buttons, RadioGroupsetc., porque todas esas clases se extienden, TextViewpor lo que deberían funcionar bien. Agregué un condicional booleano para usar la reflexión porque parece muy hack y podría comprometer notablemente el rendimiento.

Nota: como se señaló, ¡esto no funcionará para contenido dinámico! Para eso, es posible llamar a este método con say an onCreateViewo getViewmethod, pero requiere un esfuerzo adicional.

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

Luego, para usarlo, haría algo como esto:

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

Espero que ayude.


44
Creo que la mejor manera sería anular la vista de texto. Este método podría ser algo redundante si tiene una aplicación con muchas vistas.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Podría ... pero de acuerdo con las especificaciones de Android, desea evitar diseños de más de 4 niveles y 100 vistas de ancho (incluso eso es bastante). La recursión puede ser mala en términos de rendimiento, pero incluso si su diseño era de 20 niveles, eso ni siquiera es significativo.
Tom

Estoy de acuerdo en que no tiene un impacto notable en el rendimiento y no estoy tratando de decir que su respuesta es incorrecta. Simplemente no me gusta recorrer todas las vistas en el diseño si no es necesario. Además, al extender TextView, si desea modificar la apariencia de otra manera, solo tendría que cambiar el código en un lugar.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Solo quería agregar que el código anterior solo cubre TextViews, pero ListViews, Alerts, Toast, Map Markers, etc. todavía usarán la fuente del sistema.
csch

2
muchachos, esta función toma 3 parámetros, cuando la llaman recursivamente pasaron 2 parámetros ??? Cual es la correcta ? ¿Qué es reflejar?
MBH

34

En resumen:

Opción # 1: Use la reflexión para aplicar la fuente (combinando la respuesta de weston y Roger 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();
            } 
        }
    }

} 

Uso en la clase de aplicación:

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

configura un estilo para forzar la aplicación de tipo de letra de fuente (basada en lovefish ):

Pre-Lollipop:

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

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

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

Opción 2: Subclase cada vista donde necesita personalizar la fuente, es decir. ListView, EditTextView, Button, etc. ( respuesta de 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);
    } 
} 

Opción 3: Implemente un rastreador de vistas que atraviese la jerarquía de vistas de su pantalla actual:

Variación # 1 ( respuesta de 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... */ }
        } 
    } 
} 

Uso:

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

Variación # 2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Opción n. ° 4: utilice la Libra de terceros llamada Caligrafía .

Personalmente, recomendaría la Opción # 4, ya que ahorra muchos dolores de cabeza.


En la Opción # 1 , ¿qué quieres decir con "sans-serif"in newMap.put(y monospacein styles?! Quiero usar una fuente personalizada llamada barana.ttf.
Dr.jacky

en la opción 1, el primer fragmento de código no funciona con el nivel 22 de API, Android 5.1.1. Pero la otra sección del código sí. ¿Cuál debería ser la condición exacta si es así?
user1510006

28

Me gustaría mejorar la respuesta de weston para API 21 Android 5.0.

Porque

Bajo API 21, la mayoría de los estilos de texto incluyen la configuración fontFamily, como:

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

Que aplica la fuente predeterminada Roboto Regular:

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

La respuesta original no aplica la fuente monoespacio, porque android: fontFamily tiene mayor prioridad que el atributo android: typeface ( referencia ). Usar Theme.Holo. * Es una solución válida, porque no hay configuración de android: fontFamily dentro.

Solución

Dado que Android 5.0 coloca el tipo de letra del sistema en la variable estática Typeface.sSystemFontMap ( referencia ), podemos usar la misma técnica de reflexión para reemplazarlo:

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

44
Sin embargo, me pregunto por qué esto no parece funcionar para mis Buttontítulos de barra de herramientas. Estoy anulando la fuente predeterminada en MainApplication de la siguiente manera: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Estoy usando Nexus 5, v5.1.1
kip2

3
Hola, gracias. Esto fue muy útil, solo una cosa: esto no funciona en Android 6 (API 22). Entonces, si el código se cambia a esto: Build.VERSION.SDK_INT == 21- Eso es solo en la API 21 prueba en Nexus con API 22
Dijo el

Todavía no puedo configurar una fuente personalizada con este método en Nexus 9
Rahul Upadhyay

2
Cualquiera que tenga un problema no funciona para 5.1+. No anule la tipografía en sus estilos de valores-v21.
Muhammad Babar

@MuhammadBabar Gracias, su solución me
alegró el

15

es muy simple ... 1.Descargue y coloque su fuente personalizada en activos ... luego escriba una clase separada para la vista de texto de la siguiente manera: aquí utilicé la fuente 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);
    }
}

}

y haga lo siguiente en 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"
      />

Sí, eso funciona :) Pero, ¿qué pasa si quiero aplicar las mismas fuentes para otras vistas / widgets restantes? ¿Necesito escribir una clase separada para todas las demás vistas (incluidos cuadros de diálogo / tostadas / barras de acción) también?
Narendra Singh

Sí, para esta solución necesita escribir clases separadas para cada una de sus vistas.
Saad Bilal

9

También sugeriría extender TextView y otros controles, pero sería mejor considerar configurar la fuente en construcciones.

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

2
Tenga cuidado al hacer esto en plataformas anteriores a 4.0: esto perderá muchos recursos debido a un error en Android: code.google.com/p/android/issues/detail?id=9904
Ryan M

¿Cómo podemos volver a los valores predeterminados? Estoy haciendo algo como esto: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {campo final staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, newTypeface); } else {campo final staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (nulo, nulo); }
Asesino

8

Me gustaría mejorar Weston 's y Roger Huang ' s respuestas de más de 21 API de Android piruleta con el tema ' Theme.AppCompat '.

Por debajo de 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>

Sobre (igual) 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>

Y el archivo de utilidad FontsOverride es el mismo que el de la respuesta de weston . He probado en estos teléfonos:

Nexus 5 (sistema Android principal Android 5.1)

ZTE V5 (android 5.1 CM12.1)

Nota XIAOMI (Android 4.4 MIUI6)

HUAWEI C8850 (Android 2.3.5 DESCONOCIDO)


8

Puede encontrar una solución brillante aquí: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Simplemente extienda las actividades de BaseActivity y escriba esos métodos. También debería mejorar las fuentes de caché como se describe aquí: https://stackoverflow.com/a/16902532/2914140 .


Después de algunas investigaciones, escribí un código que funciona en Samsung Galaxy Tab A (Android 5.0). Código utilizado de weston y Roger Huang, así como https://stackoverflow.com/a/33236102/2914140 . También probado en Lenovo TAB 2 A10-70L, donde no funciona. Inserté una fuente 'Comic Sans' aquí para ver la diferencia.

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

Para ejecutar el código en toda la aplicación, debe escribir en alguna clase como Aplicación lo siguiente:

    new FontsOverride(this).loadFonts();

Cree una carpeta 'fuentes' dentro de 'activos' y coloque las fuentes necesarias allí. Puede encontrar una instrucción simple aquí: https://stackoverflow.com/a/31697103/2914140 .

El dispositivo Lenovo también obtiene incorrectamente el valor de una tipografía. En la mayoría de los casos, devuelve Typeface.NORMAL, a veces nulo. Incluso si TextView está en negrita (en diseño de archivo xml). Ver aquí: TextView isBold siempre devuelve NORMAL . De esta manera, un texto en una pantalla siempre tiene una fuente regular, no negrita o cursiva. Así que creo que es un error de un productor.


Estudié el código fuente de Android, tu respuesta es la mejor. Puede reemplazar delgado, mediano, liviano o cualquier otra cosa. Muchas gracias!
Mohammad Omidvar

6

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

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>

6

A partir de Android O, ¡ahora es posible definirlo directamente desde el XML y ahora mi error está cerrado!

Ver aquí para más detalles

TL; DR:

Primero debes agregar tus fuentes al proyecto

En segundo lugar, agrega una familia de fuentes, así:

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

Finalmente, puede usar la fuente en un diseño o estilo:

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

¡Disfrutar!


Gracias. Deberíamos usar Android Studio 2.4 para poder agregar una carpeta de fuentes.
Punto de fotones

en realidad solo necesito aplicarlo a mi estilo y se aplicará a través de la aplicación. pero algunos (los controles agregados mediante programación deberán aplicarse a través del código de Java)
SolidSnake

4

Puede establecer fuentes personalizadas para cada diseño uno por uno, con solo una llamada de función de cada diseño pasando su Vista raíz. Primero, cree un enfoque singelton para acceder a objetos de fuente como este

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

}

Puede definir diferentes fuentes en la clase anterior. Ahora defina una clase de ayuda de fuente que aplicará las fuentes:

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

        }

    }

}

En el código anterior, solo estoy aplicando fuentes en textView y EditText, también puede aplicar fuentes en otros elementos de vista de manera similar. Solo necesita pasar la identificación de su grupo de Vista raíz al método de fuente de aplicación anterior. Por ejemplo, su diseño es:

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

En el diseño anterior, la identificación del padre raíz es "Padre principal" ahora permite aplicar la fuente

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

Salud :)


Agradable ... no más recreación de la misma fuente, solo 1 uso de fuente para todos los campos. :)
Arfan Mirza

3

Sugeriría extender TextView y siempre usar su TextView personalizado dentro de sus diseños XML o donde sea que necesite un TextView. En su TextView personalizado, anulesetTypeface

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

2

La solución de Tom funciona muy bien, pero solo funciona con TextView y EditText.

Si desea cubrir la mayoría de las vistas (RadioGroup, TextView, Checkbox ...), creé un método que hace eso:

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

Este método recuperará el estilo de la vista establecida en XML (negrita, cursiva ...) y los aplicará si existen.

Para ListView, siempre creo un adaptador y configuro la fuente dentro de getView.


2

Escribí una clase asignando tipo de letra a las vistas en la jerarquía de vista actual y basé las propiedades del tipo de letra actual (negrita, normal, puede agregar otros estilos si lo desea):

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

Ahora en todos los fragmentos en onViewCreated o onCreateView, en todas las actividades en onCreate y en todos los adaptadores de vista en getView o newView solo invoque:

typefaceAssigner.assignTypeface(view);

2

en la api 26 con build.gradle 3.0.0 y superior, puede crear un directorio de fuentes en res y usar esta línea en su estilo

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

para cambiar build.gradle, use esto en sus dependencias build.gradle

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

y como insertar la fuente en el proyecto?
azerafati

@azerafati copia tu fuente en res / font
Mohammad Hosein Heidari

si, tanx. Ya lo tengo. Pero eso itemsolo no establece la fuente para toda la aplicación. ¿Cualquier pista?
azerafati

1

Finalmente, Google se dio cuenta de la gravedad de este problema (aplicando una fuente personalizada a los componentes de la interfaz de usuario) e ideó una solución limpia para ello.

Primero, debe actualizar para admitir la biblioteca 26+ (es posible que también necesite actualizar su gradle {4.0+}, android studio), luego puede crear una nueva carpeta de recursos llamada font. En esta carpeta, puede poner sus recursos de fuente (.tff, ...). Luego, debe anular la aplicación predeterminada y forzar su fuente personalizada en ella :)

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

Nota: si desea admitir dispositivos con una API anterior a 16, ¡debe usar el espacio de nombres de la aplicación en lugar de Android!


0

También me gustaría mejorar la respuesta de weston para API 21 Android 5.0.

Tuve el mismo problema en mi Samsung s5, cuando utilicé la fuente DEFAULT. (con las otras fuentes funciona bien)

Logré hacerlo funcionar configurando el tipo de letra ("sans" por ejemplo) en archivos XML, para cada Vista de Texto o Botón

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

y en MyApplication Class:

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

Espero eso ayude.



0

La caligrafía funciona bastante bien, pero no es adecuada para mí, ya que no admite diferentes pesos (negrita, cursiva, etc.) para una familia de fuentes.

Así que probé Fontain , que le permite definir Vistas personalizadas y aplicarlas a las familias de fuentes personalizadas.

para usar Fontain, debe agregar lo siguiente a su módulo de aplicación build.gradle:

compile 'com.scopely:fontain:1.0.0'

Luego, en lugar de usar TextView normal, debe usar FontTextView

Ejemplo de FontTextView con contenido en mayúsculas y negrita:

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

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

    }

}

No es una solución perfecta. Utilizando esta solución requerirá soluciones personalizadas para el acceso por defecto Android TextViews
blueware

0

Para cambiar la familia de fuentes predeterminada de TextViews, anule textViewStyle en el tema de su aplicación.

Para usar fuentes personalizadas en fontFamily, use recursos de fuentes que se encuentran en la biblioteca de soporte.

La función se agregó en Android 26, pero se incluyó en versiones anteriores a través de 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


0

Desde el lanzamiento de Android Oreo y su biblioteca de soporte (26.0.0) puede hacerlo fácilmente. Consulte esta respuesta en otra pregunta.

Básicamente su estilo final se verá así:

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


-15

Sí, es posible configurar la fuente para toda la aplicación.

La forma más fácil de lograr esto es empaquetar las fuentes deseadas con su aplicación.

Para hacer esto, simplemente cree un activo / carpeta en la raíz del proyecto y coloque sus fuentes (en TrueType, o TTF, formulario) en los activos.

Podría, por ejemplo, crear activos / fuentes / y colocar sus archivos TTF allí.

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);
}
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.