Как я могу изменить цвет части TextView? - PullRequest
82 голосов
/ 27 октября 2010
text = text + CepVizyon.getPhoneCode() + "\n\n"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText();
    activationText.setText(text);   
myTextView.setText(text);

Я хочу изменить цвет строки CepVizyon.getPhoneCode().Как я могу это сделать?

Ответы [ 9 ]

143 голосов
/ 26 августа 2011

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);
63 голосов
/ 27 октября 2010
myTextView.setText(Html.fromHtml(text + "<font color=white>" + CepVizyon.getPhoneCode() + "</font><br><br>"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText()));
41 голосов
/ 29 августа 2017

Если у вас есть статический текст, который нуждается в цвете, вы можете добавить его без кода через файл строк:

<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 поддерживают это

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

14 голосов
/ 17 января 2011

Что касается ответа Маниша, это будет работать, но вам нужно добавить и экранировать кавычки для атрибута цвета.

myTextView.setText(Html.fromHtml(text + "<font color=\"#FFFFFF\">" + CepVizyon.getPhoneCode() + "</font><br><br>"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText()));
5 голосов
/ 17 сентября 2015

Это хорошо для меня!

            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);
3 голосов
/ 16 августа 2018

Я сделал так, как сказал Энди Бут, но у меня также был кликабельный диапазон, и он не работал, потому что был вызван порядок setSpans.Поэтому сначала нужно вызвать spannable.setSpan(clickableSpanand..., а затем spannable.setSpan(new ForegroundColorSpan..., чтобы получить цвет в TextView

3 голосов
/ 23 июля 2018

Мне не понравилась идея делать это с помощью кода каждый раз, когда я хочу раскрасить части текста, которые я много делал во всех своих приложениях (и поскольку в некоторых случаях текст задается во время выполнения с помощьюразличные встроенные цвета), поэтому я создал свой собственный 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>
  • Любой контент, так какНе нужно вводить стиль «ничего» * ​​1043 *

Теперь для атрибутов мы собираемся использовать этот ..

([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 и экранируйте " с \.

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

3 голосов
/ 11 апреля 2018

Вот функция colorize, основанная на ответе andyboot:

<code> /**
 * 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
 * );
 * 
* @param text Текст, содержащий подстроку для раскрашивания * @param word Подстрока для раскрашивания * @param argb Цвет * @ возврат Spannable для потребления TextView * / public static Spannable colorized (окончательный строковый текст, окончательное строковое слово, конечный int argb) { окончательный Spannable spannable = новый SpannableString (текст); int substringStart = 0; int start; в то время как ((старт = text.indexOf (слово, substringStart))> = 0) { spannable.setSpan ( новый ForegroundColorSpan (argb), start, start + word.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ); substringStart = start + word.length (); } возвратный }
0 голосов
/ 27 октября 2010

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

...