Як я можу використовувати TypefaceSpan або StyleSpan із власним шрифтом?


Відповіді:


149

Ну, я не міг зрозуміти, як це зробити за допомогою доступних класів, тому я продовжив це TypefaceSpanсамостійно, тепер це працює для мене. Ось що я зробив:

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

1
@notme, що я повинен передати сімейству змінних рядків у цьому конструкторі CustomTypefaceSpan (сімейство рядків, тип шрифту) {} ???
KJEjava48,

102

Хоча Notme має, по суті, правильну ідею, наведене рішення є трохи хитрим, оскільки "сім'я" стає зайвою. Це також дещо неправильно, оскільки TypefaceSpan - це один із особливих інтервалів, про який Android знає і очікує певної поведінки щодо інтерфейсу ParcelableSpan (який підклас notme не працює належним чином, а також неможливо реалізувати).

Більш простим і точним рішенням буде:

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
+1 Дякую! І ось приклад правильного використання.
caw

@MarcoW та @Benjamin .... Бенджамін каже, що ви не можете використовувати, TypefaceSpanале тоді Марко показує робочий приклад, використовуючи саме це. Який правильний? Бенджамін ти перевіряв свій приклад?
Джейсон Мінар

@JaysonMinard Я думаю, що @MarcoW прокоментував неправильну відповідь. Припускаю, він мав намір прокоментувати відповідь @ notme, оскільки саме його клас він використовував. Щоб бути зрозумілим, я не кажу, що ви не можете підклас TypefaceSpan. Я просто дуже настійно рекомендую вам цього не робити . Це порушує принцип заміщення Ліскова і є надзвичайно поганою практикою. Ви здійснили підклас класу, який визначає шрифт через familyім'я та піддається парцеляції; тоді ви повністю переписали таку поведінку і зробили familyпараметр заплутаним і безглуздим, а Spanтакож не можна парцелювати.
Benjamin Dobell

Я справді нічого не знаю на цю тему @BenjaminDobell ... просто намагаюся зрозуміти, що відбувається в цьому іншому питанні, яке в основному перенесло відповіді тут на Котліна і не змогло змусити його працювати. Відмінність Котліна не актуальна, оскільки це однакова концепція, і це буде той самий байт-код, що і Java, щось інше повинно бути не так: stackoverflow.com/questions/35039686/…
Джейсон Мінар

@JaysonMinard Ну, якщо врахувати, що ця відповідь вже має 42 голоси, запитання, чи перевірив я свій приклад, є трохи своєрідним. Однак так, я протестував цей код. Це точний код , який я використовував в численних опублікованих додатках Android.
Benjamin Dobell

4

На Android P можна використовувати той самий клас TypefaceSpan, який вам відомий, як показано тут .

Але в старих версіях ви можете використовувати те, що вони показали пізніше у відео, про яке я писав тут .


0

Якщо когось цікавить, ось версія C # Xamarin коду Бенджаміна:

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

І використання: (в діяльності 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;  

0

Шрифт, який можна розгортати: Для того, щоб встановити інший шрифт шрифту для певної частини тексту, можна використовувати власний TypefaceSpan, як показано в наступному прикладі:

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

Однак для того, щоб згаданий код працював, клас CustomTypefaceSpan повинен бути похідним від класу 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);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.