Utilisation d'une police personnalisée dans Android TextView à l'aide de XML

92

J'ai ajouté un fichier de polices personnalisé à mon dossier assets / fonts. Comment l'utiliser à partir de mon XML?

Je peux l'utiliser à partir du code comme suit:

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

Ne puis-je pas le faire à partir de XML en utilisant un android:typeface="/fonts/Molot.otf"attribut?

Nilanchal
la source
J'ai beaucoup cherché cela et il n'y a aucun moyen que vous puissiez le faire à partir de xml.
Cd le
2
essayez de consulter ce post stackoverflow.com/questions/2376250/…
dor506
Jetez un œil à cette réponse ! Il vous permet d'utiliser plusieurs polices et d'utiliser XML.
Rafa0809
Comme d'autres l'ont dit ci-dessous, vous pouvez utiliser la calligraphie pour y parvenir.
mbonnin
Consultez cet article gadgetsaint.com/tips/…
ASP

Réponses:

45

Réponse courte: Non. Android ne prend pas en charge l'application de polices personnalisées aux widgets de texte via XML.

Cependant, il existe une solution de contournement qui n'est pas très difficile à mettre en œuvre.

Première

Vous devrez définir votre propre style. Dans votre dossier / res / values, ouvrez / créez le fichier attrs.xml et ajoutez un objet pouvant être déclaré comme suit:

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

Seconde

En supposant que vous souhaitiez utiliser souvent ce widget, vous devez configurer un cache simple pour les Typefaceobjets chargés , car les charger à partir de la mémoire à la volée peut prendre du temps. Quelque chose comme:

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

Celui-ci vous permettra d'utiliser les extensions de fichier .ttc, mais il n'a pas été testé.

Troisième

Créez une nouvelle classe qui sous-classe TextView. Cet exemple particulier tient compte de la police de caractères XML défini ( bold, italic, etc.) et l' appliquer à la police ( en supposant que vous utilisez un fichier .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));
            }
        }
    }
}

finalement

Remplacez les instances de TextViewdans votre XML par le nom de classe complet. Déclarez votre espace de noms personnalisé comme vous le feriez pour l'espace de noms Android. Notez que "typefaceAsset" doit pointer vers un fichier .ttf ou .ttc contenu dans votre répertoire / 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>
thésal
la source
Très bonne réponse! Une question cependant: pourquoi êtes-vous revenu quand isInEditMode?
Avi Shukron
04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ FATAL EXCEPTION: Processus principal: com.example.demo, PID: 3506 android.view.InflateException: Ligne de fichier XML binaire # 2: Erreur classe gonflante com.example.demo.libraries.LatoTextView chez android.view.LayoutInflater.createView (LayoutInflater.java:620) chez android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) chez android.view.LayoutInflater.inflate ( LayoutInflater.java:469) à android.view.LayoutInflater.inflate (LayoutInflater.java:397) à ...
e-info128
@AvrahamShukron - C'est là parce que j'ai continué à recevoir une erreur dans Android Studio en mode aperçu (probablement parce qu'il ne sait pas comment gérer l'application d'une police personnalisée.
themarshal
Ajout de xmlns: custom = " schemas.android.com/tools " dans RelativeLayout et l'erreur a disparu. Thanks man
Naveed Ahmad
1
Bonjour @themarshal: au lieu de xmlns: custom = " schemas.android.com/tools ", vous devriez utiliser: xmlns: custom = " schemas.android.com/apk/res-auto " afin d'utiliser des attributs stylables.
Abhinav Saxena
28

Voici un exemple de code qui fait cela. J'ai la police définie dans une variable finale statique et le fichier de police est dans le répertoire des actifs.

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
la source
5
L'OP déclare spécifiquement qu'il sait comment définir la police par programme (il fournit même un exemple). La question est de savoir comment définir la police en xml?
SMBiggs
13

Créez votre TextView personnalisé appartenant à la police que vous souhaitez utiliser. Dans cette classe, j'utilise un champ statique mTypeface pour mettre en cache la police (pour de meilleures performances)

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

}

Dans un fichier xml:

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

En classe java:

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

Activity implémente LayoutInflater.Factory2 qui fournit des rappels sur chaque vue créée. Il est possible de styliser le TextView avec l'attribut Famille de polices personnalisées, de charger les polices à la demande et d'appeler automatiquement setTypeface sur les vues de texte instanciées.

Malheureusement, en raison de la relation architecturale des instances d'Inflater par rapport aux activités et à Windows, l'approche la plus simple pour utiliser des polices personnalisées dans Android consiste à mettre en cache les polices chargées au niveau de l'application.

L'exemple de base de code est ici:

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

résulte en

entrez la description de l'image ici

Leo
la source
Je n'ai pas touché à ce code pendant 2 ans. Mais ça devrait. Je ne vois rien en théorie qui l'empêche.
Leo
7

Pas une bonne idée d'utiliser des polices personnalisées en XML en raison de ce fait qui est, vous devez le faire par programme pour éviter la fuite de mémoire!

type-a1pha
la source
6
Il semble que la fuite de mémoire ait été corrigée dans Ice Cream Sandwich.
Michael Scheper
2
Vous devez utiliser la méthode TypedArray recycle () pour éviter les fuites de mémoire.
Abhinav Saxena
7

MISE À JOUR: https://github.com/chrisjenx/Calligraphy semble être une solution supérieure à cela.


Peut-être pouvez-vous utiliser la réflexion pour injecter / pirater votre police dans la liste statique des polices disponibles lorsque votre application est créée? Je suis intéressé par les commentaires des autres si c'est une très, très mauvaise idée ou si c'est une excellente solution - il semble que ce sera l'un de ces extrêmes ...

J'ai pu injecter ma police de caractères personnalisée dans la liste des polices système avec mon propre nom de famille de polices, puis en spécifiant ce nom de famille de polices personnalisé ("brush-script") comme valeur d' android: FontFamily sur un TextView standard fonctionnait sur mon LG G4 sous 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;
    }
}

Dans ma mise en page

<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
la source
Cela ne fonctionne pas pour moi, pouvez-vous donner plus de détails sur son utilisation? La MyApplicationclasse doit-elle être appelée?
Dadan
@Yawz vous devrez appeler la méthode injectTypeface au moins une fois dans votre application. S'il enregistre une exception, je serais intéressé par les détails. Je n'ai testé cela que sur mon LG G4 sous Android 6.0 (je faisais certaines de mes propres recherches à l'époque) et je pense que cela ne fonctionne pas sur toutes les versions d'Android.
Ross Bradbury
@RossBradbury cela ne fonctionne pas sur certains appareils .. la plupart des appareils fonctionnent correctement mais certains appareils n'acceptent pas cette famille de polices .. mon appareil testé - modèle lenova a7000
Ranjith Kumar
MISE À JOUR: github.com/chrisjenx/Calligraphy est une solution supérieure.
Ross Bradbury
4

Créez un dossier de polices dans les actifs et ajoutez-y toutes vos polices requises.

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

et ajoutez un déclarable dans attrs.xml

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

puis ajoutez votre customFont comme

app:customFont="arial.ttf"
AndroidGeek
la source
vous pouvez également personnaliser la classe ci-dessus pour travailler avec le style comme cela est fait ici. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
AndroidGeek
4

Je sais que c'est une vieille question, mais j'ai trouvé une solution beaucoup plus simple.

Déclarez d'abord votre TextView en xml comme d'habitude. Mettez votre police (TTF ou TTC) dans le dossier des ressources

app \ src \ main \ assets \

Ensuite, définissez simplement la police de caractères de votre vue texte dans votre méthode 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);
}

Terminé.

Oiproks
la source
1
La question indique clairement de l'utiliser dans des fichiers xml non par programme
Gustavo Baiocchi Costa
0

au lieu de xmlns: custom = "schemas.android.com/tools"; vous devez utiliser: xmlns: custom = "schemas.android.com/apk/res-auto"; afin d'utiliser des attributs stylables. J'ai fait ce changement et cela fonctionne maintenant.

Abhinav Saxena
la source