Usando una fuente personalizada en Android TextView usando xml

92

He agregado un archivo de fuente personalizado a mi carpeta de recursos / fuentes. ¿Cómo lo uso desde mi XML?

Puedo usarlo desde el código de la siguiente manera:

TextView text = (TextView) findViewById(R.id.textview03);
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Molot.otf");
text.setTypeface(tf);

¿No puedo hacerlo desde XML usando un android:typeface="/fonts/Molot.otf"atributo?

Nilanchal
fuente
He buscado mucho en esto y no hay forma de que pueda hacerlo desde xml.
Cd
2
intente revisar esta publicación stackoverflow.com/questions/2376250/…
dor506
¡Eche un vistazo a esta respuesta ! Te permite usar múltiples fuentes y usar XML.
Rafa0809
Como otros dijeron a continuación, puede usar Caligrafía para lograr esto.
mbonnin
Consulte este artículo gadgetsaint.com/tips/…
ASP

Respuestas:

45

Respuesta corta: No. Android no tiene soporte integrado para aplicar fuentes personalizadas a widgets de texto a través de XML.

Sin embargo, existe una solución que no es muy difícil de implementar.

primero

Deberá definir su propio estilo. En su carpeta / res / values, abra / cree el archivo attrs.xml y agregue un objeto declare-styleable como este:

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

Segundo

Suponiendo que desee utilizar este widget con frecuencia, debe configurar una caché simple para los Typefaceobjetos cargados , ya que cargarlos desde la memoria sobre la marcha puede llevar tiempo. Algo como:

public class FontManager {
    private static FontManager instance;

    private AssetManager mgr;

    private Map<String, Typeface> fonts;

    private FontManager(AssetManager _mgr) {
        mgr = _mgr;
        fonts = new HashMap<String, Typeface>();
    }

    public static void init(AssetManager mgr) {
        instance = new FontManager(mgr);
    }

    public static FontManager getInstance() {
        if (instance == null) {
            // App.getContext() is just one way to get a Context here
            // getContext() is just a method in an Application subclass
            // that returns the application context
            AssetManager assetManager = App.getContext().getAssets();
            init(assetManager);
        }
        return instance;
    }

    public Typeface getFont(String asset) {
        if (fonts.containsKey(asset))
            return fonts.get(asset);

        Typeface font = null;

        try {
            font = Typeface.createFromAsset(mgr, asset);
            fonts.put(asset, font);
        } catch (Exception e) {

        }

        if (font == null) {
            try {
                String fixedAsset = fixAssetFilename(asset);
                font = Typeface.createFromAsset(mgr, fixedAsset);
                fonts.put(asset, font);
                fonts.put(fixedAsset, font);
            } catch (Exception e) {

            }
        }

        return font;
    }

    private String fixAssetFilename(String asset) {
        // Empty font filename?
        // Just return it. We can't help.
        if (TextUtils.isEmpty(asset))
            return asset;

        // Make sure that the font ends in '.ttf' or '.ttc'
        if ((!asset.endsWith(".ttf")) && (!asset.endsWith(".ttc")))
            asset = String.format("%s.ttf", asset);

        return asset;
    }
}

Este le permitirá usar extensiones de archivo .ttc, pero no está probado.

Tercero

Cree una nueva clase que subclases TextView. Este ejemplo en particular tiene en cuenta el tipo de letra XML definido ( bold, italic, etc.) y aplicarlo a la fuente (asumiendo que usted está utilizando un archivo .ttc).

/**
 * TextView subclass which allows the user to define a truetype font file to use as the view's typeface.
 */
public class FontText extends TextView {
    public FontText(Context context) {
        this(context, null);
    }

    public FontText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FontText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontText);

        if (ta != null) {
            String fontAsset = ta.getString(R.styleable.FontText_typefaceAsset);

            if (!TextUtils.isEmpty(fontAsset)) {
                Typeface tf = FontManager.getInstance().getFont(fontAsset);
                int style = Typeface.NORMAL;
                float size = getTextSize();

                if (getTypeface() != null)
                    style = getTypeface().getStyle();

                if (tf != null)
                    setTypeface(tf, style);
                else
                    Log.d("FontText", String.format("Could not create a font from asset: %s", fontAsset));
            }
        }
    }
}

Finalmente

Reemplace las instancias de TextViewen su XML con el nombre de clase completo. Declare su espacio de nombres personalizado como lo haría con el espacio de nombres de Android. Tenga en cuenta que el "typefaceAsset" debe apuntar a un archivo .ttf o .ttc contenido en su directorio / assets.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.FontText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is a custom font text"
        custom:typefaceAsset="fonts/AvenirNext-Regular.ttf"/>
</RelativeLayout>
el mariscal
fuente
¡Gran respuesta! Sin embargo, una pregunta: ¿Por qué regresó cuando isInEditMode?
Avi Shukron
04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ EXCEPCIÓN FATAL: principal Proceso: com.example.demo, PID: 3506 android.view.InflateException: Línea de archivo XML binario # 2: Error inflando la clase com.example.demo.libraries.LatoTextView en android.view.LayoutInflater.createView (LayoutInflater.java:620) en android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) en android.view.LayoutInflater.inflate ( LayoutInflater.java:469) en android.view.LayoutInflater.inflate (LayoutInflater.java:397) en ...
e-info128
@AvrahamShukron - Eso está ahí porque seguía recibiendo un error en Android Studio mientras estaba en modo de vista previa (presumiblemente porque no sabe cómo manejar la aplicación de un tipo de letra personalizado.
themarshal
Se agregó xmlns: custom = " schemas.android.com/tools " en RelativeLayout y el error desapareció. Gracias hombre
Naveed Ahmad
1
Hola @themarshal: en lugar de xmlns: custom = " schemas.android.com/tools " debes usar: xmlns: custom = " schemas.android.com/apk/res-auto " para poder usar atributos con estilo.
Abhinav Saxena
28

Aquí hay un código de ejemplo que hace esto. Tengo la fuente definida en una variable final estática y el archivo de fuente está en el directorio de activos.

public class TextViewWithFont extends TextView {

    public TextViewWithFont(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context) {
        super(context);
        this.setTypeface(MainActivity.typeface);
    }

}
Stephan Wiesner
fuente
5
El OP indica específicamente que sabe cómo configurar la fuente mediante programación (incluso proporciona un ejemplo). La pregunta es ¿cómo configurar la fuente en xml?
SMBiggs
13

Cree su TextView personalizado que pertenece a la fuente que desea utilizar. En esta clase, uso un campo mTypeface estático para almacenar en caché el tipo de letra (para un mejor rendimiento)

public class HeliVnTextView extends TextView {

/*
 * Caches typefaces based on their file path and name, so that they don't have to be created every time when they are referenced.
 */
private static Typeface mTypeface;

public HeliVnTextView(final Context context) {
    this(context, null);
}

public HeliVnTextView(final Context context, final AttributeSet attrs) {
    this(context, attrs, 0);
}

public HeliVnTextView(final Context context, final AttributeSet attrs, final int defStyle) {
    super(context, attrs, defStyle);

     if (mTypeface == null) {
         mTypeface = Typeface.createFromAsset(context.getAssets(), "HelveticaiDesignVnLt.ttf");
     }
     setTypeface(mTypeface);
}

}

En archivo xml:

<java.example.HeliVnTextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        ... />

En la clase java:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());
haotang
fuente
11

Activity implementa LayoutInflater.Factory2 que proporciona devoluciones de llamada en cada Vista creada. Es posible diseñar TextView con el atributo Familia de fuentes personalizadas, cargar los tipos de letra a pedido y llamar a setTypeface en vistas de texto instanciadas automáticamente.

Desafortunadamente, debido a la relación arquitectónica de las instancias de Inflater en relación con Actividades y Windows, el enfoque más simple para usar fuentes personalizadas en Android es almacenar en caché las fuentes cargadas en el nivel de la Aplicación.

La base de código de muestra está aquí:

https://github.com/leok7v/android-textview-custom-fonts

  <style name="Baroque" parent="@android:style/TextAppearance.Medium">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">#F2BAD0</item>
    <item name="android:textSize">14pt</item>
    <item name="fontFamily">baroque_script</item>
  </style>

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:custom="http://schemas.android.com/apk/res/custom.fonts"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
  >
  <TextView
    style="@style/Baroque"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/sample_text"
  />

resultados en

ingrese la descripción de la imagen aquí

León
fuente
No toqué ese código durante 2 años. Pero debería. No veo nada en teoría que lo impida.
Leo
7

No es una buena idea usar fuentes personalizadas en xml debido a este hecho , es decir, ¡debe hacerlo mediante programación para evitar la pérdida de memoria!

type-a1pha
fuente
6
Parece que la pérdida de memoria se solucionó en Ice Cream Sandwich.
Michael Scheper
2
Debe utilizar el método de TypedArray reciclar () para evitar pérdidas de memoria.
Abhinav Saxena
7

ACTUALIZACIÓN: https://github.com/chrisjenx/Calligraphy parece ser una solución superior a esto.


¿Quizás pueda usar la reflexión para inyectar / piratear su fuente en la lista estática de fuentes disponibles cuando se crea su aplicación? Estoy interesado en los comentarios de los demás si esta es una idea realmente mala o si es una gran solución , parece que va a ser uno de esos extremos ...

Pude inyectar mi tipo de letra personalizado en la lista de tipos de letra del sistema con mi propio nombre de familia de fuentes, luego especificando ese nombre de familia de fuentes personalizado ("brush-script") como el valor de android: FontFamily en un TextView estándar funcionó en mi LG G4 con Android 6.0.

public class MyApplication extends android.app.Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        Typeface font = Typeface.createFromAsset(this.getResources().getAssets(),"fonts/brush-script.ttf");
        injectTypeface("brush-script", font);
    }

    private boolean injectTypeface(String fontFamily, Typeface typeface)
    {
        try
        {
            Field field = Typeface.class.getDeclaredField("sSystemFontMap");
            field.setAccessible(true);
            Object fieldValue = field.get(null);
            Map<String, Typeface> map = (Map<String, Typeface>) fieldValue;
            map.put(fontFamily, typeface);
            return true;
        }
        catch (Exception e)
        {
            Log.e("Font-Injection", "Failed to inject typeface.", e);
        }
        return false;
    }
}

En mi diseño

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>
Ross Bradbury
fuente
No me funciona, ¿puedes dar más detalles sobre cómo usarlo? ¿Es MyApplicationnecesario llamar a la clase?
Dadan
@Yawz, necesitaría llamar al método injectTypeface al menos una vez en su aplicación. Si registra una excepción, estaría interesado en los detalles. Solo probé esto en mi LG G4 con Android 6.0 (estaba haciendo algunas de mis propias investigaciones en ese momento) y espero que no funcione en todas las versiones de Android.
Ross Bradbury
@RossBradbury no funciona en algunos dispositivos ... la mayoría de los dispositivos funcionan correctamente pero algunos dispositivos no aceptan esta familia de fuentes ... mi dispositivo probado - modelo lenova a7000
Ranjith Kumar
ACTUALIZACIÓN: github.com/chrisjenx/Calligraphy es una solución superior.
Ross Bradbury
4

Cree una carpeta de fuentes en los activos y agregue todas las fuentes requeridas allí.

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

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

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

    public CustomTextView(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.CustomTextView);
        String customFont = a.getString(R.styleable.CustomTextView_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String fontName) {
        Typeface typeface = null;
        try {
            if(fontName == null){
                fontName = Constants.DEFAULT_FONT_NAME;
            }
            typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + fontName);
        } catch (Exception e) {
            Log.e(TAG, "Unable to load typeface: "+e.getMessage());
            return false;
        }
        setTypeface(typeface);
        return true;
    }
}

y agregue un declarable en attrs.xml

<declare-styleable name="CustomTextView">
      <attr name="customFont" format="string"/>
</declare-styleable>

y luego agregue su customFont like

app:customFont="arial.ttf"
AndroidGeek
fuente
también puede personalizar la clase anterior para trabajar con estilo como se hace aquí. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
AndroidGeek
4

Sé que esta es una pregunta antigua, pero he encontrado una solución mucho más sencilla.

Primero declare su TextView en xml como de costumbre. Coloque su fuente (TTF o TTC) en la carpeta de activos

aplicación \ src \ main \ assets \

Luego, simplemente configure el tipo de letra para su vista de texto en su método onCreate.

@Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_name);    

    TextView textView = findViewById(R.id.my_textView);
    Typeface typeface = Typeface.createFromAsset(getAssets(), "fontName.ttf");
    textView.setTypeface(typeface);
}

Hecho.

Oiproks
fuente
1
La pregunta establece claramente que se debe usar dentro de archivos xml no programáticamente
Gustavo Baiocchi Costa
0

en lugar de xmlns: custom = "schemas.android.com/tools"; debe usar: xmlns: custom = "schemas.android.com/apk/res-auto"; para utilizar atributos de estilo. Hice este cambio y ahora está funcionando.

Abhinav Saxena
fuente