Wie kann ich die Farbe eines Teils einer Textansicht ändern?

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

Ich möchte die Farbe für CepVizyon.getPhoneCode()die Zeichenfolge ändern . Wie kann ich das machen?

atasoyh
quelle
Mögliches Duplikat von Set color of TextView span in Android
Suragch
Diese Frage wurde am 19. Juli 10 um 16:27 Uhr gestellt, ungefähr 3 Monate vor Ihrer. Es ist jedoch nicht immer der älteste Beitrag, der das doppelte Ziel sein muss. Die Anzahl der Ansichten, die Anzahl der Stimmen, die Anzahl der Antworten und die Klarheit der Frage sollten berücksichtigt werden. Wenn Sie dies als Duplikat markieren, können die Benutzer die anderen Antworten finden, die auch Ihre Frage beantworten.
Suragch
Überprüfen Sie diese stackoverflow.com/a/57089362/6667442
Ketan Ramani
Um wirklich zu verstehen, was sich hinter den Kulissen verbirgt,
empfehle

Antworten:

169

Spannable ist flexibler:

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);
Andy Boot
quelle
3
Vielen Dank für diese Antwort! Dies ähnelt eher NSAttributedString in iOS :) Um noch flexibler zu sein, ersetzen Sie text.lenght durch text2.indexOf (CepVizyon.getPhoneCode ()), sodass Sie den ersten Teil des Strings nicht kennen.
iGranDav
1
Sie sollten ()nach setzen, text.lengthda lengthes sich bei einer Methode nicht um ein Feld handelt. Würde es selbst tun, aber Änderungen müssen aus mindestens 6 Zeichen bestehen :)
MSX
Dies ist bei weitem die beste Antwort.
Pau Arlandis Martinez
1
Das Problem mit Spannable ist, dass ellipsize = end nicht mehr funktioniert. Was in einigen Fällen ein ziemlich ernstes Problem ist.
Juan Carlos Ospina Gonzalez
1
Funktioniert gut. Obwohl das Erstellen einer HTML-Zeichenfolge ratsam ist. Und dann über die HTML-Klasse analysieren. Html.fromHtml(R.id.your_html_string);
Sud007
71
myTextView.setText(Html.fromHtml(text + "<font color=white>" + CepVizyon.getPhoneCode() + "</font><br><br>"
            + getText(R.string.currentversion) + CepVizyon.getLicenseText()));
Maneesh
quelle
60

Wenn Sie statischen Text haben, der Farbe benötigt, können Sie ihn ohne Code über die Zeichenfolgendatei hinzufügen:

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

dann

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

Ergebnis

Geben Sie hier die Bildbeschreibung ein

Ich bin mir nicht sicher, auf welchen API-Versionen dies funktioniert, aber nicht für API 19, die ich bisher getestet habe. Daher unterstützen dies wahrscheinlich nur einige der neuesten API-Versionen

Bearbeiten: Wie in den Kommentaren erwähnt, versuchen Sie, @hairraisin fgcoloranstelle colorder Schriftfarbe zu verwenden. Dann sollte es für niedrigere API-Ebenen funktionieren, es müssen jedoch weitere Tests durchgeführt werden, um sicherzugehen

Fonix
quelle
3
Ich habe erfolgreich getestet mit <font fgcolor=...auf API 15 und API 25 (I nicht speziell 19 getestet hat , obwohl)
Haar Rosine
Funktioniert nicht, wenn ich den Text programmgesteuert einstelle. :(
Rohit Singh
Dies ist keine ideale Lösung, da hier Übersetzungen mit Textfarben gemischt werden.
Miloš Černilovský
15

In Bezug auf Maneeshs Antwort funktioniert dies, aber Sie müssen die Anführungszeichen für das Farbattribut hinzufügen und maskieren.

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

Für mich ist das super!

            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);
Anh Duy
quelle
4

Hier ist eine colorizeFunktion, die auf der Antwort von andyboot basiert:

 /**
 * 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;
}
Johnny Lambada
quelle
4

Hier eine Lösung in Kotlin, mit SpannableStringder die Farbe eines Teils eines Strings geändert wird.

    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
Dmitrii Leonov
quelle
1
Danke dir. Einfach elegante Lösung für Kotlin.
Nhon Nguyen
3

Ich mochte die Idee nicht, dies jedes Mal per Code zu tun, wenn ich Teile des Textes einfärben möchte, was ich in all meinen Apps oft getan habe (und da in einigen Fällen der Text zur Laufzeit mit verschiedenen Inline- eingestellt wird). definierte Farben) also habe ich meine eigenen erstellt MarkableTextView.

Die Idee war:

  • Erkennen Sie XML-Tags anhand von Zeichenfolgen
  • Identifizieren und Abgleichen des Tag-Namens
  • Extrahieren und speichern Sie Attribute und Position von Text
  • Tag entfernen und Inhalt behalten
  • Durch Attribute iterieren und Stile anwenden

Hier ist der Prozess Schritt für Schritt:

Zuerst brauchte ich eine Möglichkeit, XML-Tags in einer bestimmten Zeichenfolge zu finden, und Regexhabe den Trick gemacht.

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

Damit das oben Genannte mit einem XML-Tag übereinstimmt, müssen die folgenden Kriterien erfüllt sein:

  • Gültiger Tag-Name wie <a> <a > <a-a> <a ..attrs..>aber nicht< a> <1>
  • Schließen eines Tags mit einem passenden Namen wie, <a></a>aber nicht<a></b>
  • Beliebiger Inhalt, da keine Notwendigkeit besteht, "nichts" zu stylen.

Nun zu den Attributen, die wir verwenden werden.

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

Es hat das gleiche Konzept und im Allgemeinen musste ich für beide nicht weit gehen, da der Compiler sich um den Rest kümmert, wenn etwas aus dem Format gerät.

Jetzt brauchen wir eine Klasse, die die extrahierten Daten enthalten kann:

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

Vor allem werden wir diesen coolen Iterator hinzufügen, den ich lange benutzt habe, um Übereinstimmungen zu durchlaufen ( ich kann mich nicht an den Autor erinnern) :

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

Zum Schluss noch das Styling. Hier ist ein sehr einfacher Styler, den ich für diese Antwort gemacht habe:

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

Und so sieht die MarkableKlasse mit den Definitionen aus:

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";
    }
}

Jetzt müssen wir nur noch auf eine Zeichenfolge verweisen, und im Grunde sollte sie so aussehen:

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

Achten Sie darauf , die Tags mit einer einzuwickeln CDATA Sectionund die Flucht "mit\ .

Ich habe dies als modulare Lösung entwickelt, um Teile des Textes auf alle möglichen Arten zu verarbeiten, ohne unnötigen Code dahinter stecken zu müssen.

Explisam
quelle
3

Ich habe getan, wie Andy Boot sagte, aber ich hatte auch eine anklickbare Spanne, und es hat nicht funktioniert, weil die Reihenfolge, in der setSpanssie aufgerufen wurden. Sie müssen also zuerst das spannable.setSpan(clickableSpanand...dann das aufrufen spannable.setSpan(new ForegroundColorSpan..., um die Farbe in der Textansicht zu erhalten

Tincho825
quelle
2

Ich habe diese kleine Funktion erstellt. Übergeben Sie einfach Ihren Text an die Farbe, die Start- und Endindizes dessen, was Sie für diesen Text einfärben möchten, und die Farbe selbst

Kotlin

   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
        }

Verwendung

txt_comment.text = colorMyText("Comentario: ${item.comentario}",0,13,Color.BLACK)
Gastón Saillén
quelle
1

Zeichenzeichen-Escapezeichen verwenden + Html.fromHtml ()

Geben Sie hier die Bildbeschreibung ein

Speichern des Strings im String-Ressourcenordner

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

Wie in TextView anzeigen?

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

Bonus:

Der String in der Ausgabe sieht folgendermaßen aus

<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>
Rohit Singh
quelle
1

Mit einer universellen Kotlin-Erweiterungsfunktion würde dies folgendermaßen aussehen:

/**
 * 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)
}
Alejandro H. Cruz
quelle
-5

Eine Möglichkeit besteht darin, sich myTextViewin wenige separate Teile aufzuteilen TextViews, von denen einer nur für den Telefoncode gilt. Dann ist die Steuerung der Farbe dieser spezifischen TextViewMethode ziemlich einfach.

Ralkie
quelle
7
Nein, Schmerz im Arsch. Die Verwendung eines Spannbaren ist der richtige Weg.
Marc DiMillo
Spannable Klasse kann das ohne Aufteilung tun
Sz-Nika Janos