如何使用自定义字体设置可跨越对象字体

我有 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

68885 次浏览

Try to set your Spannable to your TextView first and then, try to assign the Typeface to your TextView with myTextView.setTypeface(tf);

This is a late answer but will help others to solve the issue.

Use the following code:(I'm using Bangla and Tamil font)

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

The outcome is:

enter image description here


CustomTypefaceSpan Class:

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

Reference

If you are using Roboto, you can set a different TypefaceSpan in the constructor

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


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

Here is an example where, str is your full string and boldString is the part you need to make bold.

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

Create a CustomTypefaceSpan class:

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

Use in the same way as the Android framework spans classes:

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

This answer is based on Imran Rana's answer but does not extend TypefaceSpan and then disable its functionality. CustomTypefaceSpan extends MetricAffectingSpan directly.

This answer shares a defect with Imran Rana's answer. The span is not parcelled. I.e if you do this (kotlin):

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

Any CustomTypefaceSpan objects set on spannableStringBuilder will not be marshalled and unmarshalled.

We don't need to use CustomTypefaceSpan. Here is the solution using 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);

In kotlin would be:

First step, create this class

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


}

Second step,

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


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

Newer, simpler answer for >= API 28

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

If you are using android KTX buildSpannable{...},

I recommend this one.

inline fun SpannableStringBuilder.font(typeface: Typeface? = null, builderAction: SpannableStringBuilder.() -> Unit) =
inSpans(StyleSpan(typeface?.style ?: Typeface.DEFAULT.style), builderAction = builderAction)

Usage

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

I wanted to do the same and I found that I need to use a TypefaceSpan. The problem is that the constructor taking a Typeface is only added in API level 28. So the workaround I found is to do the following:

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)