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

时间:2011-01-27 16:33:27

标签: android text html

我还没有办法做到这一点。有可能吗?

6 个答案:

答案 0 :(得分:142)

好吧,我无法弄清楚如何使用可用的类来完成它,所以我自己扩展了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);
    }
}

答案 1 :(得分:94)

虽然notme基本上是正确的想法,但由于“家庭”变得多余,因此给出的解决方案有点苛刻。它也有点不正确,因为TypefaceSpan是Android知道的特殊跨度之一,并且期望与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);
    }
}

答案 2 :(得分:3)

在Android P上,可以使用您所知道的相同TypefaceSpan类,如here所示。

但是在旧版本中,您可以使用他们稍后在视频中显示的内容,我已经写过here

答案 3 :(得分:0)

如果有人对本杰明代码的C#Xamarin版本感兴趣:

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

和用法:(在活动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;  

答案 4 :(得分:0)

可扩展字体:为了在文本的某些部分设置不同的字体,可以使用自定义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 );

但是,为了使上述代码正常工作,必须从类TypefaceSpan派生出CustomTypefaceSpan类。可以按照以下步骤进行操作:

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

答案 5 :(得分:-2)

我尝试了几种类似的解决方案,发现This简单易行。只需将项目单击处理为按钮单击而不是onOptionsItemSelected。谢谢!

这是我项目的代码:

在我的menu_main.xml中:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">


<item
    android:id="@+id/action_friends"
    android:orderInCategory="100"
    android:title="@string/hello_world"
    app:actionViewClass="android.widget.Button"
    app:showAsAction="always" />


<item
    android:id="@+id/action_settings"
    android:orderInCategory="100"
    android:title="@string/action_settings"
    app:showAsAction="never" />

</menu>

在我的MainActivity.java中:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    //Use custom menu
    MenuInflater inflater = getMenuInflater();
    //Inflate the custom menu
    inflater.inflate(R.menu.menu_main, menu);
    //reference to the item of the menu
    MenuItem i=menu.findItem(R.id.action_friends);
    Button itemuser =(Button) i.getActionView();

    if(itemuser!=null){
        // Create Typeface object to use unicode font in assets folder
        Typeface a =  Typeface.createFromAsset(getApplicationContext(), "fontawesome-webfont.ttf");
        // Set unicode font to menu item
        itemuser.setTypeface(a);
        itemuser.setText(getString(R.string.fa_users));
        // Set item text and color
        itemuser.setTextColor(Color.BLACK);
        // Make item background transparent
        itemuser.setBackgroundColor(Color.TRANSPARENT);

        itemuser.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                //set action when clicked
            }
        });
    }
    return super.onCreateOptionsMenu(menu);
}