如何在同一部分文本上的 TextView 的文本上设置多个跨度?

Posted

技术标签:

【中文标题】如何在同一部分文本上的 TextView 的文本上设置多个跨度?【英文标题】:How to set multiple spans on a TextView's text on the same partial text? 【发布时间】:2013-02-05 12:57:54 【问题描述】:

假设我有下一个文本:

你好堆栈溢出

我希望将第二个单词设置为RelativeSizeSpan(设置相对字体大小)和TextAppearanceSpan(设置文本颜色),如何合并它们?

我只知道我可以选择其中一个,例如使用下一个代码:

final SpannableString textToShow = new SpannableString("Hello ***");
textToShow.setSpan(new RelativeSizeSpan(1.5f), textToShow.length() - "***".length(),textToShow.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(textToShow);

但我还需要设置颜色,甚至添加其他跨越类的其他功能......

我能做什么?

【问题讨论】:

另见***.com/a/41953808 【参考方案1】:

我知道这是对一个已经回答的问题的新回复,但我想分享一个我制作的实用程序类,它使这项任务更容易。


Java 版本

public class SimpleSpanBuilder 
    private class SpanSection
        private final String text;
        private final int startIndex;
        private final CharacterStyle[] styles;

        private SpanSection(String text, int startIndex,CharacterStyle... styles)
            this.styles = styles;
            this.text = text;
            this.startIndex = startIndex;
        

        private void apply(SpannableStringBuilder spanStringBuilder)
            if (spanStringBuilder == null) return;
            for (CharacterStyle style : styles)
                spanStringBuilder.setSpan(style, startIndex, startIndex + text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
            
        
    

    private List<SpanSection> spanSections;
    private StringBuilder stringBuilder;

    public SimpleSpanBuilder()
        stringBuilder = new StringBuilder();
        spanSections = new ArrayList<>();
    

    public SimpleSpanBuilder append(String text,CharacterStyle... styles)
        if (styles != null && styles.length > 0) 
            spanSections.add(new SpanSection(text, stringBuilder.length(),styles));
        
        stringBuilder.append(text);
        return this;
    

    public SimpleSpanBuilder appendWithSpace(String text,CharacterStyle... styles)
        return append(text.concat(" "),styles);
    

    public SimpleSpanBuilder appendWithLineBreak(String text,CharacterStyle... styles)
        return append(text.concat("\n"),styles);
    

    public SpannableStringBuilder build()
        SpannableStringBuilder ssb = new SpannableStringBuilder(stringBuilder.toString());
        for (SpanSection section : spanSections)
            section.apply(ssb);
        
        return ssb;
    

    @Override
    public String toString() 
        return stringBuilder.toString();
    

用法:

SimpleSpanBuilder ssb = new SimpleSpanBuilder();
ssb.appendWithSpace("Hello");
ssb.append("***",new ForegroundColorSpan(Color.RED),new RelativeSizeSpan(1.5));
textView.setText(ssb.build());

Kotlin 版本

class SimpleSpanBuilder() 

    class Span 
        private var startIndex: Int = 0
        internal var text: String
        private var styles: Array<out CharacterStyle>

        internal constructor(index: Int, text: String, vararg styles: CharacterStyle) 
            this.startIndex = index
            this.text = text
            this.styles = styles
        

        constructor(text: String, vararg styles: CharacterStyle) : this(0, text, *styles)

        internal fun setIndex(index: Int): Span 
            return Span(index, this.text, *this.styles)
        

        internal fun apply(spanStringBuilder: SpannableStringBuilder?) 
            if (spanStringBuilder == null) return
            for (style in styles) 
                spanStringBuilder.setSpan(
                    style,
                    startIndex,
                    startIndex + text.length,
                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE
                )
            
        
    

    private val spanSections = mutableListOf<Span>()
    private val stringBuilder = StringBuilder()

    constructor(text: String, vararg styles: CharacterStyle) : this() 
        plus(Span(text, *styles))
    

    operator fun plus(span: SimpleSpanBuilder.Span): SimpleSpanBuilder 
        spanSections.add(span.setIndex(stringBuilder.length))
        stringBuilder.append(span.text)
        return this
    

    fun build(): SpannableStringBuilder 
        val ssb = SpannableStringBuilder(stringBuilder.toString())
        for (section in spanSections) 
            section.apply(ssb)
        
        return ssb
    

    override fun toString(): String 
        return stringBuilder.toString()
    

用法

var ssb = SimpleSpanBuilder("Hello ",ForegroundColorSpan(Color.BLUE))
ssb += SimpleSpanBuilder.Span(
    "***",
    ForegroundColorSpan(Color.RED),
    RelativeSizeSpan(1.5f)
)

textView.text = ssb.build()

【讨论】:

这实际上是一个不错的解决方案。 +1 的努力。 如何设置文本点击监听 @PrinkalKumar 使用ClickableSpan尝试更新答案 另请注意,如果没有textView.movementMethod = LinkMovementMethod.getInstance()ClickableSpan 将无法工作 嗨,你支持图片吗?【参考方案2】:

最简单的方法?

textView.setText("I love coding");

setHighLightedText(textView,"coding");

只需使用以下方法 -

public void setHighLightedText(TextView tv, String textToHighlight) 
    String tvt = tv.getText().toString();
    int ofe = tvt.indexOf(textToHighlight, 0);
    Spannable wordToSpan = new SpannableString(tv.getText());

    for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) 
        ofe = tvt.indexOf(textToHighlight, ofs);
        if (ofe == -1)
            break;
        else 
            // you can change or add more span as per your need
            wordToSpan.setSpan(new RelativeSizeSpan(2f), ofe,ofe + textToHighlight.length(), 0); // set size
            wordToSpan.setSpan(new ForegroundColorSpan(Color.RED), ofe, ofe + textToHighlight.length(), 0);// set color
            tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
        
    

【讨论】:

这很好,但我已经接受了一个很好的答案。但是,我会给你 +1 的努力。【参考方案3】:

Kotlin 可以通过 SpannableStringBuilder 上的扩展来帮助做到这一点:

fun SpannableStringBuilder.spansAppend(
    text: CharSequence,
    flags: Int,
    vararg spans: Any
): SpannableStringBuilder 
    val start = length
    append(text)

    spans.forEach  span ->
        setSpan(span, start, length, flags)
    

    return this

使用示例:

val builder = SpannableStringBuilder()
builder.append("Start of string ")
builder.spansAppend(
    "text spanned",
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
    RelativeSizeSpan(1.1f),
    ForegroundColorSpan(Color.RED)
)

【讨论】:

您能否展示一两个如何使用您的代码的示例?我不确定我是否理解它的作用...... @androiddeveloper 我添加了一个例子 哦,这很好。感谢您的工作和努力。有一天可能会很有帮助。我希望我会在需要时再次检查它。这样有用的东西的短代码。我会为此给你 +1。 等等,这是一个已经存在的功能?今天有人给我写了这个:***.com/a/52331300/878126 不知道这个。 inSpan 来自 Android-KTX github.com/android/android-ktx/blob/master/src/main/java/…【参考方案4】:

只需设置额外的跨度。必要时它们将重叠/合并。这段代码对我有用:

final SpannableString text = new SpannableString("Hello ***");
text.setSpan(new RelativeSizeSpan(1.5f), text.length() - "***".length(), text.length(),
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new ForegroundColorSpan(Color.RED), 3, text.length() - 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(text);

【讨论】:

我脑海中的随机数。它在这里只是为了表明您可以有多个相互交叉的跨度 但是我怎样才能添加多个 ForegroundcolorSpan? @Zielony 如果我创建了一个新课程怎么办?它应该扩展 ReplacementSpan 还是其他东西,以便我能够将它们中的多个放在同一个文本上? 据我所知,算法并不关心类型 详细例子可以看这个link

以上是关于如何在同一部分文本上的 TextView 的文本上设置多个跨度?的主要内容,如果未能解决你的问题,请参考以下文章

如何从小部件上的 TextView 获取默认文本大小?

如何在 TextView BOLD 上制作特定文本

无法在 Android Studio 中格式化部分 TextView 或 Button 文本

同一个Textview组件中的多个文本方向

TextView 上的 setText 将覆盖以前的文本

在TextView中紧紧包裹多行文本背景中的间隙