如何使用TypefaceSpan或StyleSpan与自定义字体?

76

我没有找到实现这个的方法。这可行吗?

5个回答

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

2
@notme 在这个构造函数 CustomTypefaceSpan(String family, Typeface type) {} 中,我应该给字符串变量 family 传递什么参数? - KJEjava48

108

尽管notme的想法基本上是正确的,但所给出的解决方案有些不太专业,因为“family”变得多余了。此外,它也略有错误,因为TypefaceSpan是Android知道的特殊span之一,并且期望与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 谢谢!这里是一个正确使用的例子:https://dev59.com/AGw15IYBdhLWcg3wcbWy#10741161 - caw
@MarcoW 和 @Benjamin .... Benjamin 说你不能使用 TypefaceSpan,但是 Marco 展示了一个有效的例子,仅使用了它。哪个是正确的?Benjamin,您测试过您的示例吗? - Jayson Minard
我对这个话题一无所知@BenjaminDobell...只是试图弄清楚在另一个问题中发生了什么,该问题基本上将答案移植到Kotlin中,并未能使其正常工作。 Kotlin的差异并不重要,因为它是相同的概念,并且将是与Java相同的字节码,必须有其他问题:http://stackoverflow.com/questions/35039686/extending-metricaffectingspan-with-kotlin - Jayson Minard
@JaysonMinard 嗯,考虑到这个答案已经有42个赞了,问我是否测试过我的示例有点奇怪。然而,是的,我已经测试过这段代码了。这是我在许多已发布的Android应用程序中使用的确切代码。 - Benjamin Dobell
这个CustomTypefaceSpan能否实现下划线? - VIN
显示剩余5条评论

5

在Android P上,可以使用您熟知的TypefaceSpan类,如此视频所示。

但是,在旧版本中,可以使用他们在视频后面展示的内容。我已经在这里写过相关内容。


2

可变字体:为了将不同的字体类型设置给某些文本部分,可以使用自定义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);
    }
}

0

如果有人感兴趣,这是Benjamin代码的C#Xamarin版本:

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

namespace Utils
{
    //https://dev59.com/LG445IYBdhLWcg3wg6tm#17961854
    /// <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;  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接