如何更改TextView的一部分颜色?

时间:2010-10-27 11:22:33

标签: android uitextview textview

text = text + CepVizyon.getPhoneCode() + "\n\n"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText();
    activationText.setText(text);   
myTextView.setText(text);

我想更改CepVizyon.getPhoneCode()字符串的颜色。我怎么能这样做?

18 个答案:

答案 0 :(得分:139)

Spannable更灵活:

String text2 = text + CepVizyon.getPhoneCode() + "\n\n"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText();

Spannable spannable = new SpannableString(text2);

spannable.setSpan(new ForegroundColorSpan(Color.WHITE), text.length(), (text + CepVizyon.getPhoneCode()).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

myTextView.setText(spannable, TextView.BufferType.SPANNABLE);

答案 1 :(得分:60)

myTextView.setText(Html.fromHtml(text + "<font color=white>" + CepVizyon.getPhoneCode() + "</font><br><br>"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText()));

答案 2 :(得分:34)

如果你有需要颜色的静态文本,你可以通过字符串文件添加它而不需要任何代码:

<string name="already_have_an_account">Already have an account? <font color='#01C6DB'>Login</font></string>

然后

<TextView
    android:layout_width="wrap_content"
    android:layout_height="64dp"
    android:text="@string/already_have_an_account"/>

结果

enter image description here

不确定哪个api版本可以使用,但是对于迄今为止我测试的api 19不起作用,所以可能只有一些最新的api版本支持这个

编辑:正如@hairraisin在评论中提到的,尝试使用fgcolor代替color作为字体颜色,然后它应该适用于较低的api级别,但需要更多测试以确保

答案 3 :(得分:14)

关于Maneesh的回答,这将有效,但您需要添加并转义颜色属性的引号。

myTextView.setText(Html.fromHtml(text + "<font color=\"#FFFFFF\">" + CepVizyon.getPhoneCode() + "</font><br><br>"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText()));

答案 4 :(得分:5)

这对我有好处!

            Spannable spannable = new SpannableString("ABC In-Network DEF");
            String str = spannable.toString();
            iStart = str.indexOf("In-Network");
            iEnd = iStart + 10;/*10 characters = in-network. */

            SpannableString ssText = new SpannableString(spannable);
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    //your code at here.
                }

                @Override
                public void updateDrawState(TextPaint ds) {
                    super.updateDrawState(ds);
                    ds.setUnderlineText(true);
                    ds.setColor(getResources().getColor(R.color.green));
                }
            };
            ssText.setSpan(clickableSpan, iStart, iEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            mTextView.setText(ssText);
            mTextView.setMovementMethod(LinkMovementMethod.getInstance());
            mTextView.setHighlightColor(Color.TRANSPARENT);
            mTextView.setEnabled(true);

答案 5 :(得分:4)

我做了这个小功能,只需将您的文本传递给颜色,要为该文本着色的开始和结束索引以及颜色本身

科特林

   private fun colorMyText(inputText:String,startIndex:Int,endIndex:Int,textColor:Int):Spannable{
            val outPutColoredText: Spannable = SpannableString(inputText)
            outPutColoredText.setSpan(
                ForegroundColorSpan(textColor), startIndex, endIndex,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            )
            return outPutColoredText
        }

用法

txt_comment.text = colorMyText("Comentario: ${item.comentario}",0,13,Color.BLACK)

答案 6 :(得分:3)

这是基于andyboot答案的colorize函数:

 /**
 * Colorize a specific substring in a string for TextView. Use it like this: <pre>
 * textView.setText(
 *     Strings.colorized("The some words are black some are the default.","black", Color.BLACK),
 *     TextView.BufferType.SPANNABLE
 * );
 * </pre>
 * @param text Text that contains a substring to colorize
 * @param word The substring to colorize
 * @param argb The color
 * @return the Spannable for TextView's consumption
 */
public static Spannable colorized(final String text, final String word, final int argb) {
    final Spannable spannable = new SpannableString(text);
    int substringStart=0;
    int start;
    while((start=text.indexOf(word,substringStart))>=0){
        spannable.setSpan(
                new ForegroundColorSpan(argb),start,start+word.length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        );
        substringStart = start+word.length();
    }
    return spannable;
}

答案 7 :(得分:3)

我不喜欢每次我要为所有应用程序做过很多事情的文本部分着色时都通过代码执行此操作的想法(由于在某些情况下,文本是在运行时使用不同的内联定义颜色),所以我创建了自己的MarkableTextView

该想法是:

  • 从字符串中检测XML标记
  • 识别并匹配标签名称
  • 提取并保存文本的属性和位置
  • 删除标签并保留内容
  • 遍历属性并应用样式

这是分步进行的过程:

首先,我需要一种在给定字符串中查找XML标签的方法,而Regex可以解决问题。

<([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\s+([^>]*))?>([^>][^<]*)</\1\s*>

要使上述内容与XML标记匹配,必须具有以下条件:

  • 有效的标签名称,例如<a> <a > <a-a> <a ..attrs..>,而不是< a> <1>
  • 关闭标签的名称与<a></a>类似,但与<a></b>不匹配
  • 任何内容,因为不需要设置“无”样式

现在,我们将使用此属性。

([a-zA-Z]+)\s*=\s*(['"])\s*([^'"]+?)\s*\2

它具有相同的概念,一般来说我都不需要走太远,因为如果格式出现问题,编译器将负责其余的工作。

现在,我们需要一个可以容纳提取数据的类:

public class MarkableSheet {

    private String attributes;
    private String content;
    private int outset;
    private int ending;
    private int offset;
    private int contentLength;

    public MarkableSheet(String attributes, String content, int outset, int ending, int offset, int contentLength) {

        this.attributes = attributes;
        this.content = content;
        this.outset = outset;
        this.ending = ending;
        this.offset = offset;
        this.contentLength = contentLength;
    }

    public String getAttributes() {
        return attributes;
    }

    public String getContent() {
        return content;
    }

    public int getOutset() {
        return outset;
    }

    public int getContentLength() {
        return contentLength;
    }

    public int getEnding() {
        return ending;
    }

    public int getOffset() {
        return offset;
    }
}

在进行其他任何操作之前,我们将添加这个我已经使用了很长时间的很酷的迭代器来遍历比赛(不记得作者)

public static Iterable<MatchResult> matches(final Pattern p, final CharSequence input) {

        return new Iterable<MatchResult>() {

            public Iterator<MatchResult> iterator() {

                return new Iterator<MatchResult>() {

                    // Use a matcher internally.
                    final Matcher matcher = p.matcher(input);

                    // Keep a match around that supports any interleaving of hasNext/next calls.
                    MatchResult pending;

                    public boolean hasNext() {

                        // Lazily fill pending, and avoid calling find() multiple times if the
                        // clients call hasNext() repeatedly before sampling via next().
                        if (pending == null && matcher.find()) {
                            pending = matcher.toMatchResult();
                        }
                        return pending != null;
                    }

                    public MatchResult next() {

                        // Fill pending if necessary (as when clients call next() without
                        // checking hasNext()), throw if not possible.
                        if (!hasNext()) { throw new NoSuchElementException(); }

                        // Consume pending so next call to hasNext() does a find().
                        MatchResult next = pending;
                        pending = null;

                        return next;
                    }

                    /** Required to satisfy the interface, but unsupported. */
                    public void remove() { throw new UnsupportedOperationException(); }
                };
            }
        };
    }

MarkableTextView:

public class MarkableTextView extends AppCompatTextView {

    public MarkableTextView(Context context) {
        super(context);
    }

    public MarkableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MarkableTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {

        // Intercept and process text
        text = prepareText(text.toString());

        super.setText(text, type);
    }

    public Spannable Markable;

    private Spannable prepareText(String text) {

        String parcel = text;
        Multimap<String, MarkableSheet> markableSheets = ArrayListMultimap.create();

        // Used to correct content position after tossing tags
        int totalOffset = 0;

        // Iterate through text
        for (MatchResult match : matches(Markable.Patterns.XML, parcel)) {

            // Get tag name
            String tag = match.group(1);

            // Match with a defined tag name "case-sensitive"
            if (!tag.equals(Markable.Tags.MARKABLE)) {

                // Break if no match
                break;
            }

            // Extract data
            String attributes = match.group(2);
            String content = match.group(3);

            int outset = match.start(0);
            int ending = match.end(0);
            int offset = totalOffset; // offset=0 since no preceded changes happened
            int contentLength = match.group(3).length();

            // Calculate offset for the next element
            totalOffset = (ending - outset) - contentLength;

            // Add to markable sheets
            MarkableSheet sheet =
                    new MarkableSheet(attributes, content, outset, ending, offset, contentLength);
            markableSheets.put(tag, sheet);

            // Toss the tag and keep content
            Matcher reMatcher = Markable.Patterns.XML.matcher(parcel);
            parcel = reMatcher.replaceFirst(content);
        }

        // Initialize spannable with the modified text
        Markable = new SpannableString(parcel);

        // Iterate through markable sheets
        for (MarkableSheet sheet : markableSheets.values()) {

            // Iterate through attributes
            for (MatchResult match : matches(Markable.Patterns.ATTRIBUTES, sheet.getAttributes())) {

                String attribute = match.group(1);
                String value = match.group(3);

                // Apply styles
                stylate(attribute,
                        value,
                        sheet.getOutset(),
                        sheet.getOffset(),
                        sheet.getContentLength());
            }
        }

        return Markable;
    }

最后是样式,这是我为此答案制作的一个非常简单的样式器:

public void stylate(String attribute, String value, int outset, int offset, int length) {

        // Correct position
        outset -= offset;
        length += outset;

        if (attribute.equals(Markable.Tags.TEXT_STYLE)) {

            if (value.contains(Markable.Tags.BOLD) && value.contains(Markable.Tags.ITALIC)) {

                Markable.setSpan(
                        new StyleSpan(Typeface.BOLD_ITALIC),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
            else if (value.contains(Markable.Tags.BOLD)) {

                Markable.setSpan(
                        new StyleSpan(Typeface.BOLD),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }

            else if (value.contains(Markable.Tags.ITALIC)) {

                Markable.setSpan(
                        new StyleSpan(Typeface.ITALIC),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }

            if (value.contains(Markable.Tags.UNDERLINE)) {

                Markable.setSpan(
                        new UnderlineSpan(),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }

        if (attribute.equals(Markable.Tags.TEXT_COLOR)) {

            if (value.equals(Markable.Tags.ATTENTION)) {

                Markable.setSpan(
                        new ForegroundColorSpan(ContextCompat.getColor(
                                getContext(),
                                R.color.colorAttention)),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
            else if (value.equals(Markable.Tags.INTERACTION)) {

                Markable.setSpan(
                        new ForegroundColorSpan(ContextCompat.getColor(
                                getContext(),
                                R.color.colorInteraction)),
                        outset,
                        length,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }

这是包含定义的Markable类的样子:

public class Markable {

    public static class Patterns {

        public static final Pattern XML =
                Pattern.compile("<([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)(?:\\s+([^>]*))?>([^>][^<]*)</\\1\\s*>");
        public static final Pattern ATTRIBUTES =
                Pattern.compile("(\\S+)\\s*=\\s*(['\"])\\s*(.+?)\\s*\\2");
    }

    public static class Tags {

        public static final String MARKABLE = "markable";

        public static final String TEXT_STYLE = "textStyle";
        public static final String BOLD = "bold";
        public static final String ITALIC = "italic";
        public static final String UNDERLINE = "underline";

        public static final String TEXT_COLOR = "textColor";
        public static final String ATTENTION = "attention";
        public static final String INTERACTION = "interaction";
    }
}

我们现在所需要的就是引用一个字符串,基本上它应该看起来像这样:

<string name="markable_string">
    <![CDATA[Hello <markable textStyle=\"underline\" textColor=\"interaction\">world</markable>!]]>
</string>

请确保用CDATA Section包装标签,并用"转义\

我将此作为模块化的解决方案,以各种方式处理文本的各个部分,而无需在后面填充不必要的代码。

答案 8 :(得分:3)

我按照安迪·鲍特所说的做,但是我也有一个可点击的跨度,并且由于调用setSpans的顺序而没有用。因此,您必须先调用spannable.setSpan(clickableSpanand...,然后调用spannable.setSpan(new ForegroundColorSpan...才能在TextView中获取颜色

答案 9 :(得分:3)

使用字符转义符+ Html.fromHtml()

enter image description here

如何将字符串存储在字符串资源文件夹中

<string name="textFromRes">
    &lt;font color="#FF0000">This is colored in red &lt;/font> This is not
</string>

如何在TextView中显示?

String text = this.getResources().getString(R.string.textFromRes);
htmlText.setText(Html.fromHtml(text));

奖金:

输出中的字符串如下所示

<string name="textFromRes">
    &lt;font color="#FF0000">This is colored in red &lt;/font> This is not
    &lt;br /&gt;
    &lt;h1> This is h1 heading &lt;/h1>
    &lt;br /&gt;
    &lt;h3> This is h2 subheading&lt;/h3>
    &lt;br /&gt;
    &lt;b> This text is bold&lt;/b>
    &lt;br /&gt;
    &lt;i> This text is italic&lt;/i>
    &lt;br /&gt;
    Android users expect your app to look and behave in a way that is
    consistent with the platform. Not only should you follow material
    design guidelines for visual and navigation patterns,
    but you should also follow quality guidelines for compatibility,
    performance, security, and more.
    &lt;br /&gt;
    &lt;br /&gt;
    The following links provide everything you need to design a high quality Android app.
</string>

答案 10 :(得分:2)

具有通用的Kotlin扩展功能,它看起来像这样:

/**
 * Change the color of a part of the text contained in this textView
 *
 * @param subStringToColorize has to already be set in the textView's text
 * @param colorResId
 */
fun TextView.colorize(subStringToColorize: String, @ColorRes colorResId: Int) {

  val spannable: Spannable = SpannableString(text)

  val startIndex = text.indexOf(subStringToColorize, startIndex = 0, ignoreCase = false)
  val endIndex = startIndex + subStringToColorize.length

  val color: Int = ContextCompat.getColor(context, colorResId)

  if (startIndex != -1) {
      spannable.setSpan(ForegroundColorSpan(color),
          startIndex,
          endIndex,
          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
      setText(spannable, TextView.BufferType.SPANNABLE)
   }
}

答案 11 :(得分:1)

受亚历杭德罗·H·克鲁兹(Alejandro H. Cruz)的回答above启发。

他的函数仅适用于单个子串匹配,我已经更新了他的方法以使用Regex,并且应该在所有匹配项上更新颜色:

fun TextView.colorizeAll(subStringToColorize: String, @ColorRes colorResId: Int) {

    val color: Int = ContextCompat.getColor(context, colorResId)

    val spannable: Spannable = SpannableString(text)

    val pattern = subStringToColorize.toRegex()

    val matches = pattern.findAll(text, 0)

    matches.forEach { match ->

        val startIndex = match.range.first

        val endIndex = match.range.last + match.range.step

        spannable.setSpan(ForegroundColorSpan(color),
                startIndex,
                endIndex,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        setText(spannable, TextView.BufferType.SPANNABLE)

    }
}

答案 12 :(得分:1)

SpannableStringBuilder builder = new SpannableStringBuilder();

    String the = "The ";
    SpannableString theSpannable= new SpannableString(the);
    builder.append(theSpannable);
    String author = "author ";
    SpannableString authorSpannable= new SpannableString(author);
    authorSpannable.setSpan(new RelativeSizeSpan(1.2f), 0,authorSpannable.length(), 0); // set size
    authorSpannable.setSpan(new ForegroundColorSpan(Color.BLACK), 0, authorSpannable.length(), 0);
    builder.append(authorSpannable);
    String has = "has ";
    SpannableString hasSpannable= new SpannableString(has);
    builder.append(hasSpannable);

    String approved = "approved ";
    SpannableString approvedSpannable= new SpannableString(approved);
    approvedSpannable.setSpan(new RelativeSizeSpan(1.2f), 0,approvedSpannable.length(), 0); // set size
    StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
    approvedSpannable.setSpan(boldSpan, 0, approvedSpannable.length() + 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    approvedSpannable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(this, R.color.CLR_PRESSED_SAVED)), 0,
            approvedSpannable.length(), 0);
    builder.append(approvedSpannable);

    String white = "your access to this share. Do you want re-access now?";
    SpannableString whiteSpannable= new SpannableString(white);
    builder.append(whiteSpannable);
    _AccessStatusText.setText(builder, TextView.BufferType.SPANNABLE);

答案 13 :(得分:0)

请勿使用 Html.fromHtml 以避免碎片化行为。 SpannableSpannableStringBuilder 是使用 ForegroundColorSpanBackgroundColorSpan 执行此操作的最佳方式,具体取决于您的要求。 HtmlHtmlCompat 标签即使样式为 colorbackground-color 也不可行,因为它不适用于所有 SDK,特别是在较低版本(例如 21)上,表情符号也是如此。

示例:

<span style=\"background-color:red\">&#11014;</span>

当上面的 Html 字符串使用 Spanned 转换为 HtmlCompat.fromHtml 并在 setText() 中使用时,该样式不适用于较旧的 SDK 版本。

答案 14 :(得分:0)

简单

    String text = "We've sent the code to ";
    String text2 = text + getEmail() + "\n\n";
    Spannable spannable = new SpannableString(text2);
    spannable.setSpan(new ForegroundColorSpan(Color.BLUE), text.length(), (text + getEmail()).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    mBinding.textViewStatus.setText(spannable, TextView.BufferType.SPANNABLE);

答案 15 :(得分:0)

我创建了这个可以从TextView调用的小助手方法:

fun TextView.attributedString(
    forText: String,
    foregroundColor: Int? = null,
    style: StyleSpan? = null
) {
  val spannable: Spannable = SpannableString(text)

  // check if the text we're highlighting is empty to abort
  if (forText.isEmpty()) {
    return
  }

  // compute the start and end indices from the text
  val startIdx = text.indexOf(forText)
  val endIdx = startIdx + forText.length

  // if the indices are out of bounds, abort as well
  if (startIdx < 0 || endIdx > text.length) {
    return
  }

  // check if we can apply the foreground color
  foregroundColor?.let {
    spannable.setSpan(
        ForegroundColorSpan(it),
        startIdx,
        endIdx,
        Spannable.SPAN_INCLUSIVE_EXCLUSIVE
    )
  }

  // check if we have a stylespan
  style?.let {
    spannable.setSpan(
        style,
        startIdx,
        endIdx,
        Spannable.SPAN_INCLUSIVE_EXCLUSIVE
    )
  }

  // apply it
  text = spannable
}

要使用它:

plateText.text = "Hello world!"

// This will color the part "world" to whatever color you have defined
// And make the word **bold**.
plateText.attributedString(
   "world",
   getColor(R.color.colorMatchingText, null),
   StyleSpan(Typeface.BOLD)
)

在API 29上进行测试,加油!

答案 16 :(得分:0)

这里是Kotlin中使用SpannableString更改字符串部分颜色的解决方案。

    val phoneCodeColor = ContextCompat.getColor(this, R.color.myColor)
    val text = SpannableStringBuilder()
        .color(phoneCodeColor) { append("${ CepVizyon.getPhoneCode() }") }
        .append("$\n\n")
        .append(getString(R.string.currentversion))
        .append(${ CepVizyon.getLicenseText() })

    activationText.text = text
    myTextView.text = text

答案 17 :(得分:-3)

一种方法是将myTextView拆分为几个单独的TextViews,其中一个仅用于电话代码。然后控制这个特定TextView的颜色非常简单。