Bagaimana cara mengubah warna bagian dari TextView?

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

Saya ingin mengubah warna untuk CepVizyon.getPhoneCode()string. Bagaimana saya bisa melakukan ini?

atasoyh
sumber
Kemungkinan duplikat warna Set rentang TextView di Android
Suragch
Pertanyaan itu ditanyakan pada 19 Juli '10 pukul 16:27, sekitar 3 bulan sebelum Anda. Namun, tidak selalu postingan terlama yang perlu menjadi target duplikat. Jumlah pandangan, jumlah suara, jumlah jawaban, dan kejelasan pertanyaan harus diperhitungkan. Dengan menandainya sebagai duplikat, ini dapat membantu orang menemukan jawaban lain yang juga menjawab pertanyaan Anda.
Suragch
Periksa stackoverflow
ini.com/a/57089362/6667442
Untuk benar-benar memahami apa yang ada di balik layar, saya selalu menyarankan untuk membaca artikel mendalam seperti ini: medium.com/androiddevelopers/…
Michal Vician

Jawaban:

170

Spannable lebih fleksibel:

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
sumber
3
Terima kasih atas jawaban ini! Ini lebih seperti NSAttributedString di iOS :) Agar lebih fleksibel, ganti text.lenght dengan text2.indexOf (CepVizyon.getPhoneCode ()) yang memungkinkan Anda tidak mengetahui bagian pertama dari String.
iGranDav
1
Anda harus meletakkan ()setelah text.lengthsebagai lengthadalah metode tidak lapangan. Akan melakukannya sendiri tetapi pengeditan harus terdiri dari minimal 6 karakter :)
MSX
Ini jawaban terbaik sejauh ini.
Pau Arlandis Martinez
1
Masalah dengan Spannable adalah elipsize = end tidak berfungsi lagi. Yang merupakan masalah yang cukup serius dalam beberapa kasus.
Juan Carlos Ospina Gonzalez
1
Bekerja dengan baik. Meskipun membuat string HTML disarankan. Dan kemudian menguraikannya melalui kelas 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()));
Maneesh
sumber
61

Jika Anda memiliki teks statis yang membutuhkan warna, Anda dapat menambahkannya tanpa kode apa pun melalui file string:

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

kemudian

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

hasil

masukkan deskripsi gambar di sini

tidak yakin versi api mana ini berfungsi, tetapi tidak berfungsi untuk api 19 yang telah diuji sejauh ini, jadi mungkin hanya beberapa versi api terbaru yang mendukung ini

edit: seperti yang disebutkan @hairraisin di komentar, coba gunakan fgcoloralih-alih coloruntuk warna font, maka itu harus berfungsi untuk level api yang lebih rendah, tetapi perlu lebih banyak pengujian untuk memastikan

Fonix
sumber
3
Saya telah berhasil menguji penggunaan <font fgcolor=...pada API 15 dan API 25 (meskipun saya tidak menguji 19 secara khusus)
kismis
Tidak berfungsi saat saya menyetel teks secara terprogram. :(
Rohit Singh
Ini bukan solusi ideal, karena menggabungkan terjemahan dengan warna teks.
Miloš Černilovský
16

Sehubungan dengan jawaban Maneesh, ini akan berhasil tetapi Anda perlu menambahkan dan menghilangkan tanda kutip untuk atribut color.

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

Itu bagus untukku!

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

Berikut solusi di Kotlin yang digunakan SpannableStringuntuk mengubah warna suatu string.

    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
sumber
1
Terima kasih. Solusi yang cukup elegan untuk Kotlin.
Nhon Nguyen
5

Inilah colorizefungsi berdasarkan jawaban 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;
}
JohnnyLambada
sumber
4

Saya tidak suka ide melakukan ini dengan kode setiap kali saya ingin mewarnai bagian teks yang telah banyak saya lakukan di semua aplikasi saya (dan karena dalam beberapa kasus teks sedang diatur dalam runtime dengan inline- berbeda- warna yang ditentukan) jadi saya membuat sendiri MarkableTextView.

Idenya adalah untuk:

  • Deteksi tag XML dari string
  • Identifikasi dan cocokkan nama tag
  • Ekstrak dan simpan atribut dan posisi teks
  • Hapus tag dan simpan konten
  • Iterasi melalui atribut dan terapkan gaya

Berikut proses langkah demi langkah:

Pertama saya membutuhkan cara untuk menemukan tag XML dalam string tertentu dan Regexmelakukan triknya ..

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

Agar cocok dengan tag XML di atas, ia harus memiliki kriteria berikut:

  • Nama tag yang valid seperti <a> <a > <a-a> <a ..attrs..>tetapi tidak< a> <1>
  • Tag penutup yang memiliki nama yang cocok seperti <a></a> tetapi tidak<a></b>
  • Konten apa pun, karena tidak perlu memberi gaya "tidak ada"

Sekarang untuk atributnya kita akan menggunakan yang ini ..

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

Ini memiliki konsep yang sama dan umumnya saya tidak perlu pergi jauh untuk keduanya karena kompiler akan mengurus sisanya jika ada yang keluar dari format.

Sekarang kita membutuhkan kelas yang dapat menampung data yang diekstrak:

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

Sebelum hal lain, kami akan menambahkan iterator keren ini yang telah lama saya gunakan untuk mengulang pertandingan (tidak dapat mengingat penulisnya) :

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

Terakhir, styling, jadi inilah styler yang sangat sederhana yang saya buat untuk jawaban ini:

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

Dan beginilah tampilan Markablekelas yang berisi definisi:

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

Yang kita butuhkan sekarang adalah mereferensikan string dan pada dasarnya akan terlihat seperti ini:

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

Pastikan untuk membungkus tag dengan CDATA Sectiondan escape "with\ .

Saya menjadikan ini sebagai solusi modular untuk memproses bagian-bagian teks dengan berbagai cara tanpa perlu memasukkan kode yang tidak perlu di belakang.

Explisam
sumber
4

Saya melakukan seperti yang dikatakan boot andy, tetapi saya juga memiliki rentang yang dapat diklik, dan itu tidak berhasil karena urutannya setSpansdipanggil. Jadi, Anda harus panggilan pertama spannable.setSpan(clickableSpanand...maka spannable.setSpan(new ForegroundColorSpan...untuk mendapatkan warna di TextView

Tincho825
sumber
4

Saya telah membuat fungsi kecil ini, cukup berikan teks Anda ke warna, indeks awal dan akhir dari apa yang ingin Anda warnai teks itu dan warnanya sendiri

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
        }

Pemakaian

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

Dengan fungsi ekstensi Kotlin tujuan umum, akan terlihat seperti ini:

/**
 * 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
sumber
1

Gunakan pelarian karakter + Html.fromHtml ()

masukkan deskripsi gambar di sini

Cara menyimpan String di folder sumber daya string

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

Bagaimana cara menampilkan di TextView?

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

Bonus:

String dalam output terlihat seperti ini

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

Salah satunya adalah dengan membaginya myTextViewmenjadi beberapa bagian TextViews, salah satunya hanya untuk kode telepon. Maka mengendalikan warna spesifik TextViewini cukup mudah.

Ralkie
sumber
7
Nah, sakit di pantat. Menggunakan spannable adalah cara yang tepat.
Marc DiMillo
Kelas yang dapat diatur dapat melakukan itu tanpa membelah
Sz-Nika Janos