I have not found a way to do this. Is it possible?
Whilst notme has essentially the right idea, the solution given is a bit hacky as "family" becomes redundant. It is also slightly incorrect because TypefaceSpan is one of the special spans that Android knows about and expects certain behaviour with respect to the ParcelableSpan interface (which notme's subclass does not properly, nor is it possible to, implement).
A simpler and more accurate solution would be:
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);
}
}
On Android P it's possible using the same TypefaceSpan class you know of, as shown here.
But on older versions, you can use what they've shown later in the video, which I've written about here.
If anybody would be interested here's C# Xamarin version of Benjamin's 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);
}
}
}
And usage: (in activity 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;
Well I couldn't figure out how to do it with the available classes so I extended the TypefaceSpan
on my own an now it works for me. Here is what I did:
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);
}
}
Spannable typeface: In order to set a different font typeface to some portion of text, a custom TypefaceSpan can be used, as shown in the following example:
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 );
However, in order to make the above code working, the class CustomTypefaceSpan has to be derived from the class TypefaceSpan. This can be done as follows:
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);
}
}