76 votes

Comment puis-je utiliser TypefaceSpan ou StyleSpan avec une police de caractères personnalisée ?

Je n'ai pas trouvé de moyen de le faire. Est-ce possible ?

149voto

notme Points 1312

Comme je n'arrivais pas à trouver comment le faire avec les classes disponibles, j'ai étendu la fonction TypefaceSpan par moi-même et maintenant ça marche pour moi. Voici ce que j'ai fait :

package de.myproject.text.style;

import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.TypefaceSpan;

public class CustomTypefaceSpan extends TypefaceSpan {
    private final Typeface newType;

    public CustomTypefaceSpan(String family, Typeface type) {
        super(family);
        newType = type;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        applyCustomTypeFace(ds, newType);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        applyCustomTypeFace(paint, newType);
    }

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        int oldStyle;
        Typeface old = paint.getTypeface();
        if (old == null) {
            oldStyle = 0;
        } else {
            oldStyle = old.getStyle();
        }

        int fake = oldStyle & ~tf.getStyle();
        if ((fake & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }

        if ((fake & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }

        paint.setTypeface(tf);
    }
}

2 votes

@notme qu'est-ce que je dois passer à la variable chaîne famille dans ce constructeur CustomTypefaceSpan(String family, Typeface type) {} ? ???

105voto

Benjamin Dobell Points 949

Bien que notme ait essentiellement la bonne idée, la solution donnée est un peu bancale car "famille" devient redondant. Elle est également légèrement incorrecte car TypefaceSpan est l'un des espaces spéciaux qu'Android connaît et attend un certain comportement par rapport à l'interface ParcelableSpan (que la sous-classe de notme n'implémente pas correctement, et qu'il n'est pas possible d'implémenter).

Une solution plus simple et plus précise serait :

public class CustomTypefaceSpan extends MetricAffectingSpan
{
    private final Typeface typeface;

    public CustomTypefaceSpan(final Typeface typeface)
    {
        this.typeface = typeface;
    }

    @Override
    public void updateDrawState(final TextPaint drawState)
    {
        apply(drawState);
    }

    @Override
    public void updateMeasureState(final TextPaint paint)
    {
        apply(paint);
    }

    private void apply(final Paint paint)
    {
        final Typeface oldTypeface = paint.getTypeface();
        final int oldStyle = oldTypeface != null ? oldTypeface.getStyle() : 0;
        final int fakeStyle = oldStyle & ~typeface.getStyle();

        if ((fakeStyle & Typeface.BOLD) != 0)
        {
            paint.setFakeBoldText(true);
        }

        if ((fakeStyle & Typeface.ITALIC) != 0)
        {
            paint.setTextSkewX(-0.25f);
        }

        paint.setTypeface(typeface);
    }
}

1 votes

+1 Merci ! Et aquí est un exemple d'utilisation correcte.

0 votes

@MarcoW et @Benjamin .... Benjamin dit que vous ne pouvez pas utiliser TypefaceSpan mais ensuite Marco montre un exemple de travail en utilisant juste ça. Lequel est le bon ? Benjamin a testé ton exemple ?

0 votes

@JaysonMinard Je pense que @MarcoW a commenté la mauvaise réponse. Je suppose qu'il voulait commenter la réponse de @notme, puisque c'est sa classe qu'il a utilisée. Pour être clair, je ne suis pas en train de dire que vous ne peut pas sous-classe TypefaceSpan . Je suis juste en train de suggérer très fortement tu ne devrais pas . Ce faisant, vous violez le principe de substitution de Liscov et c'est une très mauvaise pratique. Vous avez sous-classé une classe qui spécifie la police de caractères par le biais d'une balise family nom y est parcellisable ; vous avez alors totalement écrasé ce comportement et fait de la family paramètre confus et inutile, et le Span est également no morcelable.

5voto

android developer Points 20939

Sur Android P, il est possible d'utiliser la même classe TypefaceSpan que celle que vous connaissez, comme indiqué ci-dessous aquí .

Mais sur les versions plus anciennes, vous pouvez utiliser ce qu'ils ont montré plus loin dans la vidéo, et que j'ai écrit à ce sujet. aquí .

0voto

Diolor Points 1388

Peut-être cette réponse peut être utile .

Je vais ajouter mon approche simple pour la cohérence. Je ne sais pas pourquoi deux des réponses ci-dessus sont longues :)

SpannableString sb = new SpannableString(getResources().getString(R.string.subtitle));
sb.setSpan(new TypefaceSpan("sans-serif-light"), 9, 15, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
sb.setSpan(new TypefaceSpan("sans-serif-light"), 24, 34, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(sb);

Explication, parce que j'ai trouvé les Docs délicats :

La magie ici est SPAN_EXCLUSIVE_INCLUSIVE avec lequel vous pouvez ajouter la même police de caractères sur différentes parties de la chaîne.

Ce qui précède fonctionne également avec SpannableStringBuilder si vous souhaitez ajouter (ajouter) du texte ou le modifier.

Veillez à expérimenter avec les drapeaux .

Pour les différentes portées, voir exemple ici .

0voto

Đonny Points 689

Si quelqu'un est intéressé, voici la version C# Xamarin du code de Benjamin :

using System;
using Android.Graphics;
using Android.Text;
using Android.Text.Style;

namespace Utils
{
    //https://stackoverflow.com/a/17961854/1996780
    /// <summary>A text span which applies <see cref="Android.Graphics.Typeface"/> on text</summary>
    internal class CustomFontSpan : MetricAffectingSpan
    {
        /// <summary>The typeface to apply</summary>
        public Typeface Typeface { get; }

        /// <summary>CTor - creates a new instance of the <see cref="CustomFontSpan"/> class</summary>
        /// <param name="typeface">Typeface to apply</param>
        /// <exception cref="ArgumentNullException"><paramref name="typeface"/> is null</exception>
        public CustomFontSpan(Typeface typeface) =>
            Typeface = typeface ?? throw new ArgumentNullException(nameof(typeface));

        public override void UpdateDrawState(TextPaint drawState) => Apply(drawState);

        public override void UpdateMeasureState(TextPaint paint) => Apply(paint);

        /// <summary>Applies <see cref="Typeface"/></summary>
        /// <param name="paint"><see cref="Paint"/> to apply <see cref="Typeface"/> on</param>
        private void Apply(Paint paint)
        {
            Typeface oldTypeface = paint.Typeface;
            var oldStyle = oldTypeface != null ? oldTypeface.Style : 0;
            var fakeStyle = oldStyle & Typeface.Style;

            if (fakeStyle.HasFlag(TypefaceStyle.Bold))
                paint.FakeBoldText = true;

            if (fakeStyle.HasFlag(TypefaceStyle.Italic))
                paint.TextSkewX = -0.25f;

            paint.SetTypeface(Typeface);
        }
    }
}

Et l'usage : (dans l'activité OnCreate)

var txwLogo = FindViewById<TextView>(Resource.Id.logo);
var font = Resources.GetFont(Resource.Font.myFont);

var wordtoSpan = new SpannableString(txwLogo.Text);
wordtoSpan.SetSpan(new CustomFontSpan(font), 6, 7, SpanTypes.InclusiveInclusive); //One caracter
txwLogo.TextFormatted = wordtoSpan;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X