web-dev-qa-db-fra.com

Utilisation de polices personnalisées dans Android TextView à l'aide de xml

J'ai ajouté un fichier de police 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);

Puis-je le faire à partir de XML en utilisant un attribut Android:typeface="/fonts/Molot.otf"?

90
npanigrahy

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 terriblement difficile à mettre en œuvre.

Premier

Vous devrez définir votre propre style. Dans votre dossier/res/values, ouvrez/créez le fichier attrs.xml et ajoutez un objet déclarable-styleable, comme ceci:

<?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 objets chargés Typeface, car leur chargement à 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 ce n'est pas testé.

Troisième

Créez une nouvelle classe qui sous-classe TextView. Cet exemple particulier prend en compte le type de caractère XML défini (bold, italic, etc.) et l'applique à 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 TextView dans votre code XML par le nom de classe qualifié complet. Déclarez votre espace de noms personnalisé, tout 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>
43
themarshal

Voici un exemple de code qui fait cela. La police est définie dans une variable finale statique et le fichier de police se trouve dans le répertoire assets.

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

}
27
Stephan Wiesner

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

Malheureusement, en raison de la relation architecturale des instances 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.

La base de code exemple 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

enter image description here

11
Leo

Créez votre TextView personnalisé pour appartenir à la police que vous souhaitez utiliser. Dans cette classe, j'utilise un champ statique mTypeface pour mettre en cache la police de caractères (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);
}

}

En 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());
11
haotang

UPDATE: https://github.com/chrisjenx/Calligraphy semble être un solution supérieure à cela.


Vous pouvez peut-être utiliser reflect pour injecter/pirater votre police dans la liste statique des polices disponibles lors de la création de votre application? Je suis intéressé par les commentaires des autres s'il s'agit d'un vraiment, vraiment une mauvaise idée ou s'il s'agit d'un excellente solution - il semble que ce soit l'un de ces extrêmes ...

J'ai été en mesure d'injecter mon caractère personnalisé dans la liste des caractères système avec mon propre nom de famille de polices, puis de spécifier ce nom de famille de polices personnalisé ("brush-script") comme valeur de Android: FontFamily sur un TextView standard travaillé 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"/>
7
Ross Bradbury

Ce n'est pas une bonne idée d'utiliser des polices personnalisées au format xml en raison de ce fait En d'autres termes, vous devez le faire par programme pour éviter la fuite de mémoire!

7
type-a1pha

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

Commencez par déclarer votre TextView au format XML comme d'habitude . Placez votre police (TTF ou TTC) dans le dossier

app\src\main\actifs \

Ensuite, définissez simplement la police de caractères pour votre affichage 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é.

3
Oiproks

Créez un dossier de polices dans les ressources et ajoutez toutes les 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 ajouter un déclarable dans attrs.xml

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

puis ajoutez votre customFont like

app:customFont="arial.ttf"
1
Xar E Ahmer

La meilleure solution consiste à utiliser (enfin) une fonctionnalité de police personnalisée native en XML introduite par Google. Mais vous devez cibler l'API 26. Il supporte l'API 16+

https://developer.Android.com/guide/topics/ui/look-and-feel/fonts-in-xml

0
Kirill Karmazin

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… j’ai fait cette modification et elle fonctionne maintenant. 

0
Abhinav Saxena