Як я можу змінити колір частини TextView?


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

Я хочу змінити колір CepVizyon.getPhoneCode()рядка. Як я можу це зробити?



Можливий копія
заданого

Це питання було задано 19 липня '10 о 16:27, приблизно за 3 місяці до вашого. Однак не завжди найстаріший пост повинен бути дублікатом цілі. Слід враховувати кількість переглядів, кількість голосів, кількість відповідей та чіткість питання. Позначивши це як дублікат, він може допомогти людям знайти ті інші відповіді, які також відповідають на ваше запитання.
Сурач


Щоб по-справжньому зрозуміти, що є за кадром, я завжди пропоную прочитати таку поглиблену статтю, як ця: medium.com/androiddevelopers/…
Міхал Віціан,

Відповіді:


170

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

3
Дякую за цю відповідь! Це більше схоже на NSAttributedString в iOS :) Щоб бути ще більш гнучким, замініть text.lenght на text2.indexOf (CepVizyon.getPhoneCode ()), який дозволяє вам не знати першої частини String.
iGranDav 02

1
Ви повинні застосувати ()після, text.lengthяк lengthце метод не поле. Зробив би це сам, але правки повинні містити принаймні 6 символів :)
MSX

Це найкраща відповідь на сьогоднішній день.
Пау Арландіс Мартінес

1
Проблема Spannable полягає в тому, що ellipsize = end більше не працює. Що в деяких випадках є досить серйозною проблемою.
Хуан Карлос Оспіна Гонсалес

1
Працює просто чудово. Хоча створення рядка HTML доцільно. А потім розбираємо його за допомогою класу HTML. Html.fromHtml(R.id.your_html_string);
sud007

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

61

Якщо у вас є статичний текст, який потребує кольору, ви можете додати його без будь-якого коду через файл рядків:

<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"/>

результат

введіть тут опис зображення

не впевнений, для яких версій api це працює, але не працює для api 19, який ive протестував дотепер, тому, ймовірно, підтримують це лише деякі останні версії api

редагувати: як @hairraisin згадувалося в коментарях, спробуйте використовувати fgcolorзамість colorкольору шрифту, тоді він повинен працювати для нижчих рівнів api, але потребує додаткового тестування, щоб бути впевненим


3
Я успішно протестував за допомогою <font fgcolor=...API 15 та API 25 (я спеціально не тестував 19)
родзинка волосся

Не працює, коли я встановлюю текст програмно. :(
Рохіт Сінгх,

Це не ідеальне рішення, оскільки воно поєднує переклади з кольорами тексту.
Miloš Černilovský

16

Що стосується відповіді Маніша, це спрацює, але вам потрібно додати та уникнути лапок для атрибута color.

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

8

Мені це добре!

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

6

Ось рішення в Котліні, яке використовує 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

1
Дякую. Просто елегантне рішення для Kotlin.
Нхон Нгуєн,

5

Ось colorizeфункція, заснована на відповіді andyboot:

 /**
 * 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;
}

4

Мені не сподобалася ідея робити це за кодом кожен раз, коли мені хочеться забарвити частини тексту, які я багато робив у всіх своїх програмах (а оскільки в деяких випадках текст встановлюється під час виконання з різними вбудованими, визначені кольори) тому я створив свою власну 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і бігти "з\ .

Я зробив це як модульне рішення для обробки частин тексту різними способами, без необхідності забивати непотрібний код.


4

Я зробив, як сказав Енді завантажувач, але у мене був також простір, що можна натиснути, і це не спрацювало, тому що setSpansбуло викликано наказ . Отже, вам потрібно спочатку зателефонувати spannable.setSpan(clickableSpanand...тоді, spannable.setSpan(new ForegroundColorSpan...щоб отримати колір у TextView


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)

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 = if (/* Your code for isMarshmallowOrUp() */ ) {
      this.context.getColor(colorResId)
  } else {
      this.context.resources.getColor(colorResId)
  }

  spannable.setSpan(ForegroundColorSpan(color),
                  startIndex,
                  endIndex,
                  Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

  this.setText(spannable, TextView.BufferType.SPANNABLE)
}

1

Використовуйте символи для втечі + Html.fromHtml ()

введіть тут опис зображення

Як зберігати String у папці ресурсних рядків

<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>

-5

Один із способів - розділити їх myTextViewна кілька окремих TextViews, один із яких - лише для телефонного коду. Тоді керування кольором цього конкретного TextViewдосить прямолінійне.


7
Ні, біль у попці. Використання перемикача - це правильний шлях.
Marc DiMillo

Розбірний клас може це зробити без розколу
Sz-Nika Janos
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.