如何使用自定义字体设置Spannable对象的字体

96

我有一个Spannable对象,我想使用之前加载的自定义字体设置它的字体。

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/font_Name.ttf");
Spannable span1 = /*Spannable Item*/;

/// I want to set span1 to have tf font face???
/// Here where I want help.

编辑:
我的问题是我想要为文本视图设置两种不同的自定义字体,所以我正在使用Spannable


重复的问题:如何在自定义字体中使用TypefaceSpan或StyleSpan?请问有没有人知道如何在自定义字体中使用TypefaceSpan或StyleSpan?我已经尝试了很多方法,但都没有成功。任何帮助将不胜感激。谢谢! - Benjamin Dobell
1
@BenjaminDobell 一个问题不能是答案的副本。不要链接到你自己的答案。 - Chad Bingham
10个回答

237

虽然回答比较晚,但可以帮助其他人解决这个问题。

使用以下代码:(我正在使用孟加拉语和泰米尔语字体)

  TextView txt = (TextView) findViewById(R.id.custom_fonts);  
        txt.setTextSize(30);
        Typeface font = Typeface.createFromAsset(getAssets(), "Akshar.ttf");
        Typeface font2 = Typeface.createFromAsset(getAssets(), "bangla.ttf");   
        SpannableStringBuilder SS = new SpannableStringBuilder("আমারநல்வரவு");
        SS.setSpan (new CustomTypefaceSpan("", font2), 0, 4,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        SS.setSpan (new CustomTypefaceSpan("", font), 4, 11,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        txt.setText(SS);
结果是: enter image description here CustomTypefaceSpan类:
package my.app;
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);
}
}

参考资料


12
好的 - 不过如果你打算在家庭中使用它,为什么不从构造函数中完全移除family参数,而是只调用super("")。 - Adam
@imran-rana 我该如何使用 CustomTypefaceSpan - mahdi pishguy
@Imran Rana,这个自定义类出现了一个lint错误。"这个类实现了Parcelable接口,但没有提供CREATOR字段。" - Krups
@Krups,如果CustomTypefaceSpan被操作系统打包和解包,那么这种方法是行不通的。 - alexbirkett
警告:此类实现了“Parcelable”,但未提供“CREATOR”字段。 - Mir-Ismaili
显示剩余2条评论

31
创建一个CustomTypefaceSpan类:
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;

public class CustomTypefaceSpan extends MetricAffectingSpan {

    private final Typeface typeface;

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

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

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

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        paint.setTypeface(tf);
    }
}

使用与Android框架跨度类相同的方式:

    TextView textView = (TextView) findViewById(R.id.custom_fonts);
    Typeface font = Typeface.createFromAsset(getAssets(), "Akshar.ttf");
    Typeface font2 = Typeface.createFromAsset(getAssets(), "bangla.ttf");
    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("আমারநல்வரவு");
    spannableStringBuilder.setSpan (new CustomTypefaceSpan(font2), 0, 4,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    spannableStringBuilder.setSpan (new CustomTypefaceSpan(font), 4, 11,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    textView.setText(spannableStringBuilder);

这个答案基于Imran Rana的回答,但不扩展TypefaceSpan并禁用其功能。 CustomTypefaceSpan 直接扩展了MetricAffectingSpan

这个答案与Imran Rana的回答一样存在缺陷。该span未被打包,也就是说,如果您执行以下操作(kotlin):

    val parcel = Parcel.obtain()
    TextUtils.writeToParcel(spannableStringBuilder, parcel, 0)
    parcel.setDataPosition(0)
    val sequence = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel)
    parcel.recycle()

SpannableStringBuilder上设置的任何CustomTypefaceSpan对象都不会被编组和解组。


21

我们不需要使用CustomTypefaceSpan。这里是使用StyleSpan的解决方案。

/**
* setCustomFontTypeSpan
* @param context
* @param source
* @param startIndex
* @param endIndex
* @param font
* @return
*/
public static SpannableString setCustomFontTypeSpan(Context context, String 
source, int startIndex, int endIndex, int font) {
      final SpannableString spannableString = new SpannableString(source);
      Typeface typeface = ResourcesCompat.getFont(context, font);
      spannableString.setSpan(new StyleSpan(typeface.getStyle()), 
      startIndex,endIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
}

String source = "Hello world";
SpannableString string = setCustomFontTypeSpan(context, source, 6, 
source.length(), R.font.open_sans_bold);
textView.setText(string);

7
StyleSpan并不用于设置字体类型。如文档所述:该Span可允许设置所附文本的样式,可能的样式有:Typeface#NORMAL,Typeface#BOLD,Typeface#ITALIC和Typeface#BOLD_ITALIC。 给定的代码将会将span设置为BOLD样式,但不会影响字体类型。 - kza

17

如果您正在使用Roboto字体,您可以在构造函数中设置不同的TypefaceSpan

TypefaceSpan typefaceSpan = new TypefaceSpan("sans-serif-medium");

textView.setSpan(typefaceSpan, indexStart, textLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

12
如果您正在使用Android KTX的buildSpannable {...},我建议使用这个。
inline fun SpannableStringBuilder.font(typeface: Typeface? = null, builderAction: SpannableStringBuilder.() -> Unit) =
    inSpans(StyleSpan(typeface?.style ?: Typeface.DEFAULT.style), builderAction = builderAction)

用法

binding.title.text = buildSpannedString {
    color(getColor(R.color.main_mint)) {
        font(getFont(R.font.notosans_medium)) {
            append("colored and typefaced")
        }
    }
}

6

我也想做相同的事情,但我发现需要使用TypefaceSpan。问题在于只有API级别28才添加了接受Typeface参数的构造函数。所以我找到的解决方法是进行以下操作:

val spannable = SpannableString("My String")
spannable.setSpan(object : TypefaceSpan(null) {
    override fun updateDrawState(ds: TextPaint) {
        ds.typeface = Typeface.create(ResourcesCompat.getFont(context, R.font.my_font), Typeface.NORMAL) // To change according to your need
    }
}, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE) // To change according to your need
myTextView.setText(spannable)

6

针对API28及以上版本的新版、更简单的解决方案

val myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme), Typeface.NORMAL)
val string = SpannableString("Text with typeface span.")
string.setSpan(TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
string.setSpan(TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

textView.text = string

5
在 Kotlin 中将是:
第一步,创建这个类。
class CustomTypefaceSpan(family: String?, private val newType: Typeface) :
  TypefaceSpan(family) {
  override fun updateDrawState(ds: TextPaint) {
      applyCustomTypeFace(ds, newType)
  }

  override fun updateMeasureState(paint: TextPaint) {
      applyCustomTypeFace(paint, newType)
  }

  companion object {
      private fun applyCustomTypeFace(paint: Paint, tf: Typeface) {
          val oldStyle: Int
          val old = paint.typeface
          oldStyle = old?.style ?: 0
          val fake = oldStyle and tf.style.inv()
          if (fake and Typeface.BOLD != 0) {
              paint.isFakeBoldText = true
          }
          if (fake and Typeface.ITALIC != 0) {
              paint.textSkewX = -0.25f
          }
          paint.typeface = tf
      }
  }

}

第二步

    val prefixText = SpannableString("I read and accept ")
    val prefixTextLen = prefixText.length

    prefixText.setSpan(
        CustomTypefaceSpan("", ResourcesCompat.getFont(context, R.font.gotham_bold)!!),
        0,
        prefixTextLen,
        0
    )

1
尝试先将您的Spannable设置到TextView中,然后使用myTextView.setTypeface(tf)TextView分配字体。

除了Serkan的回答之外,我写了一篇博客文章详细介绍如何使用spans,网址为http://blog.stylingandroid.com/archives/177。它并没有特别涵盖设置字体,这就是为什么我没有将其发布为答案的原因,但它确实涵盖了一般spans中的一些常见陷阱。 - Mark Allison
1
但这只是在整个TextView上设置字体?它不是一个span。 - alexbirkett

-1

以下是一个示例,其中str是您的完整字符串,而boldString是需要加粗的部分。

public static SpannableString getTextStyleSpan(String str, String boldString) {

        SpannableString formated = new SpannableString(str);

        int start1 = str.indexOf(boldString);
        int end1 = start1 + colorString1.length();

        formated.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), start1, end1, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        return formated;
    }

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