如何使用支持库字体功能作为TextView内容的一部分(使用spannable)?

and*_*per 10 fonts android textview spannable android-typeface

背景

支持库(此处为docs )允许您在XML中使用"res/font"文件夹中的TTF字体文件:

app:fontFamily="@font/lato_black"
Run Code Online (Sandbox Code Playgroud)

或通过代码:

val typeface = ResourcesCompat.getFont(context, R.font.lato_black)
Run Code Online (Sandbox Code Playgroud)

问题

虽然我知道可以使用spannable技术在TextView内容的部分内容中设置不同的样式(例如粗体,斜体,颜色等等),但我发现设置不同字体的唯一方法就是使用内置在操作系统的字体,如这里,但我不能看到一个办法做到这一点的加载字体的新途径.

我试过的

我试图找到一种方法来转换两者,但没有运气.当然,我也试图在可能的功能的文档中找到,并且我试图通过互联网找到它.

这个问题

如何为TextView的不同部分设置不同的字体?

例如,在文本"Hello world"中,将"Hello"设置为"lato_black"的字体,其余为默认值.


编辑:由于我得到了来自两个不同的人的相同答案,我不能接受另一个.给他们两个+1的努力,我已经改变了一点问题:

我如何轻松地将字体样式设置为文本的一部分,同时让strings.xml文件使用自定义字体标记定义它.

例如,这可以在strings.xml文件中进行设置,如上所述:

<string name="something" ><customFont fontResource="lato_black">Hello</customFont> world</string>
Run Code Online (Sandbox Code Playgroud)

然后,在代码中,您要做的就是使用以下内容:

textView.setText (Html.fromHtml(text, null, CustomFontTagHandler()))
Run Code Online (Sandbox Code Playgroud)

我认为这非常重要,因为翻译后的字符串可能与英语中的字符串差别太大,因此您不能只解析字符串的文本,然后选择设置自定义字体的位置.它必须在strings文件中.

and*_*per 19

由于这两个答案MJMTheMatrix实际上是相同的(但过于复杂的我),两者都在同一时间回答,我不能只选择其中的一个,所以我理所当然+1每个,要求他们把它缩短但支持XML标记,以便更轻松地处理字符串文件.

目前,这是如何为TextView中的部分文本设置自定义字体的更短版本:

class CustomTypefaceSpan(private val typeface: Typeface?) : MetricAffectingSpan() {
    override fun updateDrawState(paint: TextPaint) {
        paint.typeface=typeface
    }

    override fun updateMeasureState(paint: TextPaint) {
        paint.typeface=typeface
    }
}
Run Code Online (Sandbox Code Playgroud)

样品用法:

    val text = "Hello world"
    val index = text.indexOf(' ')
    val spannable = SpannableStringBuilder(text)
    spannable.setSpan(CustomTypefaceSpan(ResourcesCompat.getFont(this, R.font.lato_light)), 0, index, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
    spannable.setSpan(CustomTypefaceSpan(ResourcesCompat.getFont(this, R.font.lato_bold)), index, text.length, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
    textView.text = spannable
Run Code Online (Sandbox Code Playgroud)

编辑:似乎谷歌提供了一个关于此的视频,在这里:

class CustomTypefaceSpan(val font: Typeface?) : MetricAffectingSpan() {
    override fun updateMeasureState(textPaint: TextPaint) = update(textPaint)
    override fun updateDrawState(textPaint: TextPaint?) = update(textPaint)

    private fun update(tp: TextPaint?) {
        tp.apply {
            val old = this!!.typeface
            val oldStyle = old?.style ?: 0
            val font = Typeface.create(font, oldStyle)
            typeface = font
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并且在视频中也讨论了在strings.xml中处理它的解决方案,这里使用注释而不是新的HTML标记.例:

strings.xml中

<string name="title"><annotation font="lato_light">Hello</annotation> <annotation font="lato_bold">world</annotation></string>
Run Code Online (Sandbox Code Playgroud)

MainActivity.kt

    val titleText = getText(R.string.title) as SpannedString
    val spannable = SpannableStringBuilder(titleText)
    val annotations = titleText.getSpans(0, titleText.length, android.text.Annotation::class.java)
    for (annotation in annotations) {
        if(annotation.key=="font"){
            val fontName=annotation.value
            val typeface= ResourcesCompat.getFont(this@MainActivity,resources.getIdentifier(fontName,"font",packageName))
            spannable.setSpan(CustomTypefaceSpan(typeface),spannable.getSpanStart(annotation),spannable.getSpanEnd(annotation), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    }
    textView.text = spannable
Run Code Online (Sandbox Code Playgroud)

结果如下:

在此输入图像描述

我仍然很确定可以使用fromHtml,但它可能不值得.

我也想知道如果我们想要使用基本HTML标签和我们为字体设置的cusomzied,如果我们确实annotation在那里使用,应该怎么做.


MJM*_*MJM 5

自定义类别适用fonrFamilySpan

 public class MultipleFamilyTypeface extends TypefaceSpan {
        private final Typeface typeFace;

        public MultipleFamilyTypeface(String family, Typeface type) {
            super(family);
            typeFace = type;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            applyTypeFace(ds, typeFace);
        }

        @Override
        public void updateMeasureState(TextPaint paint) {
            applyTypeFace(paint, typeFace);
        }

        private static void applyTypeFace(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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

应用字体

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String firstWord = "Hello ";
        String secondWord = "Word ";
        String thirdWord = "Normal ";

        TextView textViewTest = findViewById(R.id.textViewTest);

        Spannable spannable = new SpannableString(firstWord + secondWord + thirdWord);

        Typeface CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.akronim);
        Typeface SECOND_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.baloo_thambi);

        spannable.setSpan(new MultipleFamilyTypeface("akronim", CUSTOM_TYPEFACE), 0, firstWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannable.setSpan(new MultipleFamilyTypeface("baloo_thambi", SECOND_CUSTOM_TYPEFACE), firstWord.length(), firstWord.length() + secondWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);


        textViewTest.setText(spannable);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出

在此处输入图片说明

自定义标签的编辑方法二

加入implementation 'org.jsoup:jsoup:1.11.3'gradle

 List<String> myCustomTag = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        TextView textViewTest = findViewById(R.id.textViewTest);


        // mention list custom tag that you used 
        myCustomTag.add("akronim");
        myCustomTag.add("baloo_thambi");
        myCustomTag.add("xyz");

        String html = "<akronim>Hello</akronim>"
                + "<baloo_thambi> Word  </baloo_thambi>"
                + " Normal "
                + " <xyz> testing </xyz> "
                + "<akronim>Styles</akronim>";
        textViewTest.setText(processToFontStyle(html));

    }


    public Spannable processToFontStyle(String text) {

        Document doc = Jsoup.parse(text);
        Elements tags = doc.getAllElements();
        String cleanText = doc.text();
        Log.d("ClearTextTag", "Text " + cleanText);
        Spannable spannable = new SpannableString(cleanText);
        List<String> tagsFromString = new ArrayList<>();
        List<Integer> startTextPosition = new ArrayList<>();
        List<Integer> endTextPosition = new ArrayList<>();
        for (Element tag : tags) {
            String nodeText = tag.text();
            if (myCustomTag.contains(tag.tagName())) {
                int startingIndex = cleanText.indexOf(nodeText);
                tagsFromString.add(tag.tagName());
                startTextPosition.add(startingIndex);
                endTextPosition.add(startingIndex + nodeText.length());
            }
        }

        Typeface CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.akronim);
        Typeface SECOND_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.baloo_thambi);
        Typeface XYZ_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.architects_daughter);


        for (int i = 0; i < tagsFromString.size(); i++) {
            String fontName = tagsFromString.get(i);
            Typeface selected = null;
            switch (fontName) {
                case "akronim":
                    selected = CUSTOM_TYPEFACE;
                    break;
                case "baloo_thambi":
                    selected = SECOND_CUSTOM_TYPEFACE;
                    break;
                case "xyz":
                    selected = XYZ_CUSTOM_TYPEFACE;
                    break;
            }
            if (selected != null)
                spannable.setSpan(new MultipleFamilyTypeface(fontName, selected), startTextPosition.get(i), endTextPosition.get(i), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        }


        return spannable;
    }
Run Code Online (Sandbox Code Playgroud)

输出

在此处输入图片说明