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);
}
}
TypefaceSpan
but then Marco shows a working example using just that. Which is the right one? Benjamin did you test your example? –
Autopsy TypefaceSpan
. I'm just very strongly suggesting you shouldn't. Doing so breaks Liscov's principal of substitution and is extremely bad practice. You've sub-classed a class that specifies the font via a family
name and is parcelable; you've then totally overwritten that behaviour and made the family
parameter confusing and pointless, and the Span
is also not parcelable. –
Physicalism Typeface
class doesn't provide support for underlines. However, both the official TypefaceSpan
and my class CustomTypefaceSpan
use a TextPaint
behind the scenes to assist with the rendering, the only problem is that it's not exposed in a simple fashion. To underline an entire TextView
you can do textView.setPaintFlags(Paint.UNDERLINE_TEXT_FLAG);
. Otherwise, add a boolean underlined
, and a new method setUnderlined(boolean)
to my class. Then in apply(Paint)
, add paint.setUnderlineText(underlined);
–
Physicalism SKPaint.cpp
you can see exactly what is going on. –
Physicalism 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);
}
}
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);
}
}
TypefaceSpan
but then Marco shows a working example using just that. Which is the right one? Benjamin did you test your example? –
Autopsy TypefaceSpan
. I'm just very strongly suggesting you shouldn't. Doing so breaks Liscov's principal of substitution and is extremely bad practice. You've sub-classed a class that specifies the font via a family
name and is parcelable; you've then totally overwritten that behaviour and made the family
parameter confusing and pointless, and the Span
is also not parcelable. –
Physicalism Typeface
class doesn't provide support for underlines. However, both the official TypefaceSpan
and my class CustomTypefaceSpan
use a TextPaint
behind the scenes to assist with the rendering, the only problem is that it's not exposed in a simple fashion. To underline an entire TextView
you can do textView.setPaintFlags(Paint.UNDERLINE_TEXT_FLAG);
. Otherwise, add a boolean underlined
, and a new method setUnderlined(boolean)
to my class. Then in apply(Paint)
, add paint.setUnderlineText(underlined);
–
Physicalism SKPaint.cpp
you can see exactly what is going on. –
Physicalism 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);
}
}
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://mcmap.net/q/219141/-how-can-i-use-typefacespan-or-stylespan-with-a-custom-typeface
/// <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;
© 2022 - 2024 — McMap. All rights reserved.