Wie kann ich TypefaceSpan oder StyleSpan mit einer benutzerdefinierten Schrift verwenden?

76

Ich habe keinen Weg gefunden, dies zu tun. Ist es möglich?

nicht ich
quelle

Antworten:

149

Nun, ich konnte nicht herausfinden, wie ich es mit den verfügbaren Klassen machen soll, also habe ich die selbst erweitert und TypefaceSpanjetzt funktioniert es für mich. Folgendes habe ich getan:

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);
    }
}
nicht ich
quelle
1
@notme Was soll ich in diesem Konstruktor an die String-Variablenfamilie übergeben? CustomTypefaceSpan (String-Familie, Schrifttyp) {} ???
KJEjava48
102

Während notme im Wesentlichen die richtige Idee hat, ist die angegebene Lösung etwas hackig, da "Familie" überflüssig wird. Es ist auch etwas falsch, da TypefaceSpan einer der speziellen Bereiche ist, die Android kennt und die ein bestimmtes Verhalten in Bezug auf die ParcelableSpan-Schnittstelle erwarten (die von der Unterklasse von notme nicht ordnungsgemäß implementiert wird oder implementiert werden kann).

Eine einfachere und genauere Lösung wäre:

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);
    }
}
Benjamin Dobell
quelle
1
+1 Danke! Und hier ist ein richtiges Anwendungsbeispiel.
Caw
@MarcoW und @Benjamin .... Benjamin sagt, dass Sie nicht verwenden können, TypefaceSpanaber dann zeigt Marco ein funktionierendes Beispiel, das genau das verwendet. Welches ist das richtige? Benjamin hast du dein Beispiel getestet?
Jayson Minard
@JaysonMinard Ich denke, @MarcoW hat die falsche Antwort kommentiert. Ich nehme an, er wollte die Antwort von @ notme kommentieren, da es seine Klasse ist, die er benutzt hat. Um es klar zu sagen, ich sage nicht, dass Sie nicht unterklassifizieren können TypefaceSpan. Ich schlage nur sehr stark vor, dass Sie nicht sollten . Dies bricht Liscovs Substitutionsprinzip und ist eine äußerst schlechte Praxis. Sie haben eine Klasse unterklassifiziert, die die Schriftart über einen familyNamen angibt und parzellierbar ist. Sie haben dieses Verhalten dann vollständig überschrieben und den familyParameter verwirrend und sinnlos gemacht, und das Spanist auch nicht paketierbar.
Benjamin Dobell
Ich weiß wirklich nichts zu diesem Thema @BenjaminDobell ... ich versuche nur herauszufinden, was in dieser anderen Frage vor sich geht, die im Grunde die Antworten hier auf Kotlin portiert und es nicht zum Laufen gebracht hat. Der Kotlin-Unterschied ist nicht relevant, da es sich um dasselbe Konzept handelt und der gleiche Bytecode wie bei Java wäre. Mit etwas anderem muss etwas falsch sein: stackoverflow.com/questions/35039686/…
Jayson Minard
@JaysonMinard Nun, wenn man bedenkt, dass diese Antwort bereits 42 Stimmen hat, ist die Frage, ob ich mein Beispiel getestet habe, etwas eigenartig. Ja, ich habe diesen Code getestet. Dies ist der genaue Code, den ich in zahlreichen veröffentlichten Android-Apps verwendet habe.
Benjamin Dobell
4

Unter Android P ist es möglich, dieselbe TypefaceSpan-Klasse zu verwenden, die Sie kennen, wie hier gezeigt .

Bei älteren Versionen können Sie jedoch das verwenden, was sie später in dem Video gezeigt haben, über das ich hier geschrieben habe .

Android-Entwickler
quelle
0

Wenn jemand interessiert wäre, hier ist die C # Xamarin-Version von Benjamins Code:

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

Und Verwendung: (in Aktivität 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;  
Đonny
quelle
0

Spannbare Schrift: Um eine andere Schriftart für einen Teil des Textes festzulegen , kann eine benutzerdefinierte Schriftart verwendet werden, wie im folgenden Beispiel gezeigt:

spannable.setSpan( new CustomTypefaceSpan("SFUIText-Bold.otf",fontBold), 0,
firstWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan( new CustomTypefaceSpan("SFUIText-Regular.otf",fontRegular),
firstWord.length(), firstWord.length() + lastWord.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setText( spannable );

Damit der obige Code funktioniert, muss die Klasse CustomTypefaceSpan von der Klasse TypefaceSpan abgeleitet werden. Dies kann wie folgt erfolgen:

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);
    }
}
Abdelaziz Daoud
quelle