Fuentes personalizadas y diseños XML (Android)


174

Estoy tratando de definir un diseño de GUI usando archivos XML en Android. Hasta donde puedo averiguar, no hay forma de especificar que sus widgets deben usar una fuente personalizada (por ejemplo, una que haya colocado en los activos / fuente /) en archivos XML y solo puede usar las fuentes instaladas en el sistema.

Sé que, en el código Java, podría cambiar la fuente de cada widget de forma manual utilizando ID únicos. Alternativamente, podría iterar sobre todos los widgets en Java para hacer este cambio, pero esto probablemente sería muy lento.

¿Qué otras opciones tengo? ¿Hay alguna forma mejor de hacer widgets que tengan un aspecto personalizado? En particular, no quiero tener que cambiar manualmente la fuente para cada nuevo widget que agregue.


68
DrDefrost: acepte algunas respuestas, obtendrá más respuestas en este sitio.
SK9

1
Aquí hay una pregunta más similar: stackoverflow.com/questions/9030204/…
Vins

Actualizado el 05/2017: "La biblioteca de soporte 26.0 Beta brinda soporte para la función Fuentes en XML en dispositivos que ejecutan la API de Android versión 14 y superior". Ver: developer.android.com/preview/features/…
albert c braun

Respuestas:


220

Puede ampliar TextView para establecer fuentes personalizadas como aprendí aquí .

TextViewPlus.java:

package com.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class TextViewPlus extends TextView {
    private static final String TAG = "TextView";

    public TextViewPlus(Context context) {
        super(context);
    }

    public TextViewPlus(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public TextViewPlus(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.TextViewPlus);
        String customFont = a.getString(R.styleable.TextViewPlus_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            Log.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

}

attrs.xml: (en res / valores)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="TextViewPlus">
        <attr name="customFont" format="string"/>
    </declare-styleable>
</resources>

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res/com.example"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.example.TextViewPlus
        android:id="@+id/textViewPlus1"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:text="@string/showingOffTheNewTypeface"
        foo:customFont="saxmono.ttf">
    </com.example.TextViewPlus>
</LinearLayout>

Pondría "saxmono.ttf" en la carpeta de activos .

ACTUALIZACIÓN 1/8/13

Hay serios problemas de memoria con este método. Ver el comentario de chedabob a continuación.


55
Eso se ve bien, sin embargo, recibo un error cuando intento usar el "TextViewPlus" en el main.xml. Me sale lo siguiente: - error: Error al analizar XML: prefijo independiente - El prefijo "foo" para el atributo "foo: customFont" asociado con un tipo de elemento "supportLibs.TextViewPlus" no está vinculado.
Majjoodi

79
Una cosa a tener en cuenta es que esto generará docenas y docenas de objetos TypeFace y consumirá memoria. Hay un error en Android anterior a 4.0 que no libera TypeFaces correctamente. Lo más fácil de hacer es crear una caché TypeFace con un HashMap. Esto redujo el uso de memoria en mi aplicación de más de 120 mb a 18 mb. code.google.com/p/android/issues/detail?id=9904
chedabob

77
@Majjoodi: Eso suele suceder si olvida agregar el segundo espacio de nombres a su diseño:xmlns:foo="http://schemas.android.com/apk/res/com.example
Theo

11
MUY IMPORTANTE: me gustaría hacer doble hincapié en el consejo de chedabob. A menos que lo siga, tendrá una pérdida de memoria en pre-ICS. Hay pocas soluciones, una de ellas está en el enlace proporcionado por chedabob, otra está aquí: stackoverflow.com/questions/8057010/listview-memory-leak . Peter - por favor actualiza tu respuesta - es genial pero no está completo
Michał K

1
¿Cómo configuro este atributo personalizado en styles.xml además de otros atributos de vista de texto como ancho y alto?
loeschg

35

Llego 3 años tarde a la fiesta :( Sin embargo, esto podría ser útil para alguien que pueda tropezar con esta publicación.

He escrito una biblioteca que almacena en caché los tipos de letra y también le permite especificar tipos de letra personalizados directamente desde XML. Puedes encontrar la biblioteca aquí .

Así es como se vería su diseño XML, cuando lo use.

<com.mobsandgeeks.ui.TypefaceTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    geekui:customTypeface="fonts/custom_font.ttf" />

Hola @ Ragunath-jawahar, ¿cómo importaría la biblioteca para un proyecto de gradle? Lo intenté compile 'com.mobsandgeeks:android-typeface-textview:1.0'pero eso no funcionó.
aimango

1
Necesitarás un AAR para poder usarlo de esa manera. Aún no está allí. Puede copiar las fuentes y crear un proyecto de biblioteca de Android Studio por ahora.
Ragunath Jawahar

2
¿De dónde viene tu etiqueta geekui?
sefirosu

3
Desde el espacio de nombres especificado en la etiqueta principal -xmlns:geekui="http://schemas.android.com/apk/res-auto"
Ragunath Jawahar

1
@MuhammedRefaat, sí lo hace :)
Ragunath Jawahar

18

Puede que sea un poco tarde, pero debe crear una clase singleton que devuelva el tipo de letra personalizado para evitar pérdidas de memoria.

Tipo de clase de rostro:

public class OpenSans {

private static OpenSans instance;
private static Typeface typeface;

public static OpenSans getInstance(Context context) {
    synchronized (OpenSans.class) {
        if (instance == null) {
            instance = new OpenSans();
            typeface = Typeface.createFromAsset(context.getResources().getAssets(), "open_sans.ttf");
        }
        return instance;
    }
}

public Typeface getTypeFace() {
    return typeface;
}
}

TextView personalizado:

public class NativelyCustomTextView extends TextView {

    public NativelyCustomTextView(Context context) {
        super(context);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

}

Por xml:

<com.yourpackage.views.NativelyCustomTextView
            android:id="@+id/natively_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="20dp"
            android:text="@string/natively"
            android:textSize="30sp" /> 

Programáticamente:

TextView programmaticallyTextView = (TextView) 
       findViewById(R.id.programmatically_text_view);

programmaticallyTextView.setTypeface(OpenSans.getInstance(this)
                .getTypeFace());

Parece funcionar en tiempo de ejecución, pero ¿se supone que también debe funcionar en el diseñador?
Magnus Johansson

Por supuesto. Se puede usar como xml. @Magnus
Leonardo Cardoso

Creo que entendiste mal, no parece funcionar en tiempo de diseño (Vista previa del diseñador) (xml! = diseñador). Funciona bien especificado en el archivo de diseño Xml compilado en tiempo de ejecución. De todos modos, estoy usando esta extensión github.com/danh32/Fontify , que funciona mucho mejor para mis necesidades (admite múltiples estilos de fuente, regular, negrita, etc., así como diferentes nombres de fuentes y otros controles más allá de TextView)
Magnus Johansson

2
GetTypeFace crea un nuevo tipo de letra en cada llamada ... esto anula el propósito del singleton. Debe tener un campo estático que se establece la primera vez que se realiza la llamada y devuelve el valor del campo en las llamadas posteriores.
Steven Pena

1
@chiwai tienes razón! Sobre la primera pregunta no la necesita. Sobre el segundo fue un error tipográfico.
Leonardo Cardoso

13

Antigua pregunta, pero desearía leer esta respuesta aquí antes de comenzar mi propia búsqueda de una buena solución. Calligraphy extiende el android:fontFamilyatributo para agregar soporte para fuentes personalizadas en su carpeta de activos, de esta manera:

<TextView 
  android:text="@string/hello_world"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:fontFamily="fonts/Roboto-Bold.ttf"/>

Lo único que tiene que hacer para activarlo es adjuntarlo al contexto de la actividad que está utilizando:

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

También puede especificar su propio atributo personalizado para reemplazar android:fontFamily

También funciona en temas, incluido el AppTheme.


8

Usando DataBinding :

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

el archivo de fuente debe estar en assets/fonts/


7

Si solo tiene un tipo de letra que desea agregar y desea escribir menos código, puede crear un TextView dedicado para su fuente específica. Ver el código a continuación.

package com.yourpackage;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class FontTextView extends TextView {
    public static Typeface FONT_NAME;


    public FontTextView(Context context) {
        super(context);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
}

En main.xml, ahora puede agregar su TextView de esta manera:

<com.yourpackage.FontTextView
    android:id="@+id/tvTimer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="" />

haga esto en init () y ahórrese 3 veces la misma llamada.
ericosg

@ericosg obtengo este error cuando uso su solución android.view.InflateException: línea de archivo XML binario # 9: Error al inflar la clase com.ascent.adwad.utils.CustomTextView
Sagar Devanga

@SagarDevanga, difícil de ayudar sin más información. Quizás lo lleves tan lejos como puedas y haz una nueva pregunta.
ericosg

7

La mejor manera de hacerlo Desde la versión de vista previa de Android O es de esta manera
1.) Haga clic derecho en la carpeta res y vaya a Nuevo> directorio de recursos de Android .
Aparece la ventana Nuevo directorio de recursos.
2.) En la lista Tipo de recurso, seleccione la fuente y luego haga clic en Aceptar.
3.) Agregue sus archivos de fuentes en la carpeta de fuentes . La estructura de carpetas a continuación genera R.font.dancing_script, R.font.la_la y R.font.ba_ba.
4.) Haga doble clic en un archivo de fuente para obtener una vista previa de las fuentes del archivo en el editor.

Luego debemos crear una familia de fuentes

1.) Haga clic derecho en la carpeta de fuentes y vaya a Nuevo> Archivo de recursos de fuentes . Aparece la ventana Nuevo archivo de recursos.
2.) Ingrese el nombre del archivo y luego haga clic en Aceptar . El nuevo recurso XML de fuente se abre en el editor.
3.) Adjunte cada archivo de fuente, estilo y atributo de peso en el elemento de etiqueta de fuente. El siguiente XML ilustra la adición de atributos relacionados con la fuente en el recurso XML de fuente:

<?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/hey_regular" />
    <font
    android:fontStyle="italic"
    android:fontWeight="400"
    android:font="@font/hey_bababa" />
</font-family>

Agregar fuentes a un TextView:

   <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    **android:fontFamily="@font/ba_ba"**/>

A partir de la documentación

Trabajando con fuentes

Todos los pasos son correctos.


1
La mejor respuesta! El nombre de archivo de la fuente debe estar en minúsculas.
Dmitry

También puede crear un estilo personalizado que aplique a su aplicación (bajo el archivo AndroidManifest) y se le dará a TODAS las vistas. En lugar de, por ejemplo, simplemente colocarlo en un solo TextView, ya que esto no afectará a la Barra de herramientas.
goldenmaza

5

Extienda TextViewy dele un atributo personalizado o simplemente use el atributo android: tag para pasar una Cadena de la fuente que desea usar. Deberá elegir una convención y cumplirla, ya que pondré todas mis fuentes en la carpeta res / assets / fonts / para que su clase TextView sepa dónde encontrarlas. Luego, en su constructor, simplemente configura la fuente manualmente después de la súper llamada.


4

La única forma de usar fuentes personalizadas es a través del código fuente.

Solo recuerde que Android se ejecuta en dispositivos con recursos y fuentes muy limitados que pueden requerir una buena cantidad de RAM. Las fuentes Droid incorporadas están especialmente hechas y, si lo notas, faltan muchos caracteres y decoraciones.


"Solo recuerda que Android se ejecuta en dispositivos con recursos muy limitados" -> cada vez es menos el caso. Teléfono de cuatro núcleos? ¿¿De Verdad??
Sandy

9
Haría más para mostrar consideración sobre el contexto de la respuesta de tareqHs, que precede a su comentario en unos 2 años y medio.
SK9

La primera parte de su respuesta puede haber sido cierta cuando escribió su respuesta en 2010. La última es superfluoys y además del punto: Droid fue malo, abandonado por Google en 2012 a favor de Roboto. La falta de elección en la tipografía en Android es una falla, no una característica; iOS ha tenido múltiples fuentes disponibles para los desarrolladores desde 2008 bajo dispositivos primitivos según los estándares actuales.
cosmix

Esta respuesta ya no es válida.
Martin Konecny

2

Podría tener una respuesta simple para la pregunta sin extender TextView e implementar un código largo.

Código:

 TextView tv = (TextView) findViewById(R.id.textview1);
    tv.setTypeface(Typeface.createFromAsset(getAssets(), "font.ttf"));

Coloque el archivo de fuente personalizado en la carpeta de activos como de costumbre e intente esto. Esto funciona para mi. Simplemente no entiendo por qué Peter ha dado un código tan grande para esta cosa simple o ha dado su respuesta en la versión anterior.


2

También se puede definir en el xml sin crear clases personalizadas

style.xml

<style name="ionicons" parent="android:TextAppearance">
    <!-- Custom Attr-->
    <item name="fontPath">fonts/ionicons.ttf</item>
</style>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/ionicons"
        android:text=""/>
</LinearLayout>

Una nota rápida, porque siempre olvidé dónde colocar las fuentes, es que la fuente debe estar dentro assetsy esta carpeta reside en el mismo nivel que resy src, en mi caso, esassets/fonts/ionicons.ttf

Se agregó el diseño de raíz agregado porque este método debe xmlns:app="http://schemas.android.com/apk/res-auto"funcionar

Actualización 2 Olvidé una biblioteca que instalé antes llamada Calligraphy


Esto no funciona para mi. Cuando intento construir esto, aparece el mensaje de error: Error: (49, 5) No se encontraron recursos que coincidan con el nombre dado: attr 'fontPath'.
Stef

Intente agregar xmlns:app="http://schemas.android.com/apk/res-auto"a su diseño raíz, verifique también la respuesta actualizada
norman784

El error no proviene del archivo XML de diseño, sino del archivo XML de estilos. Parece que no "sabe" qué es un fontPath.
Stef

Tienes razón, olvidé que tengo una biblioteca llamada Calligrapy
norman784

1

La respuesta de Peter es la mejor, pero puede mejorarse usando styles.xml de Android para personalizar sus fuentes para todas las vistas de texto en su aplicación.

Mi codigo esta aqui


1

Hay dos formas de personalizar las fuentes:

!!! mi fuente personalizada en assets / fonts / iran_sans.ttf

Camino 1: Refrection Typeface.class |||mejor manera

llamar a FontsOverride.setDefaultFont () en la clase extiende la aplicación, este código hará que se cambien todas las fuentes de software, incluso las fuentes Toast

AppController.java

public class AppController extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        //Initial Font
        FontsOverride.setDefaultFont(getApplicationContext(), "MONOSPACE", "fonts/iran_sans.ttf");

    }
}

FontsOverride.java

public class FontsOverride {

    public static void setDefaultFont(Context context, String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(), fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    }

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

Camino 2: use setTypeface

para una vista especial solo llame a setTypeface () para cambiar la fuente.

CTextView.java

public class CTextView extends TextView {

    public CTextView(Context context) {
        super(context);
        init(context,null);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    }

    public void init(Context context, @Nullable AttributeSet attrs) {

        if (isInEditMode())
            return;

        // use setTypeface for change font this view
        setTypeface(FontUtils.getTypeface("fonts/iran_sans.ttf"));

    }
}

FontUtils.java

public class FontUtils {

    private static Hashtable<String, Typeface> fontCache = new Hashtable<>();

    public static Typeface getTypeface(String fontName) {
        Typeface tf = fontCache.get(fontName);
        if (tf == null) {
            try {
                tf = Typeface.createFromAsset(AppController.getInstance().getApplicationContext().getAssets(), fontName);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            fontCache.put(fontName, tf);
        }
        return tf;
    }

}


0

Puede crear fácilmente una clase de vista de texto personalizada: -

Entonces, lo que debes hacer primero, crea una Custom textviewclase que se extienda con AppCompatTextView.

public class CustomTextView extends AppCompatTextView {
    private int mFont = FontUtils.FONTS_NORMAL;
    boolean fontApplied;

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, context);
    }

    public CustomTextView(Context context) {
        super(context);
        init(null, context);
    }

    protected void init(AttributeSet attrs, Context cxt) {
        if (!fontApplied) {
            if (attrs != null) {
                mFont = attrs.getAttributeIntValue(
                        "http://schemas.android.com/apk/res-auto", "Lato-Regular.ttf",
                        -1);
            }
            Typeface typeface = getTypeface();
            int typefaceStyle = Typeface.NORMAL;
            if (typeface != null) {
                typefaceStyle = typeface.getStyle();
            }
            if (mFont > FontUtils.FONTS) {
                typefaceStyle = mFont;
            }
            FontUtils.applyFont(this, typefaceStyle);
            fontApplied = true;
        }
    }
}

Ahora, cada vez que la vista de texto personalizado llame y obtendremos el valor int del atributo int fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1).

O

También podemos obtener getTypeface () de la vista que configuramos en nuestro xml ( android:textStyle="bold|normal|italic"). Así que haz lo que quieras hacer.

Ahora, FontUtilsestablecemos cualquier fuente .ttf en nuestra vista.

public class FontUtils {

    public static final int FONTS = 1;
    public static final int FONTS_NORMAL = 2;
    public static final int FONTS_BOLD = 3;
    public static final int FONTS_BOLD1 = 4;

    private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

    static Typeface getFonts(Context context, String name) {
        Typeface typeface = TYPEFACE.get(name);
        if (typeface == null) {
            typeface = Typeface.createFromAsset(context.getAssets(), name);
            TYPEFACE.put(name, typeface);
        }
        return typeface;
    }

    public static void applyFont(TextView tv, int typefaceStyle) {

        Context cxt = tv.getContext();
        Typeface typeface;

        if(typefaceStyle == Typeface.BOLD_ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }else if (typefaceStyle == Typeface.BOLD || typefaceStyle == SD_FONTS_BOLD|| typefaceStyle == FONTS_BOLD1) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-SemiBold.ttf");
        } else if (typefaceStyle == Typeface.ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Thin.ttf");
        } else {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }
        if (typeface != null) {
            tv.setTypeface(typeface);
        }
    }
}

0

Puede ser útil saber que a partir de Android 8.0 (API nivel 26) puede usar una fuente personalizada en XML .

Puede aplicar una fuente personalizada a toda la aplicación de la siguiente manera.

  1. Pon la fuente en la carpeta res/font.

  2. En res/values/styles.xmluso en el tema de la aplicación. <style name="AppTheme" parent="{whatever you like}"> <item name="android:fontFamily">@font/myfont</item> </style>



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.