TextView’un bir bölümünün rengini nasıl değiştirebilirim?


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

Dizesinin rengini değiştirmek istiyorum CepVizyon.getPhoneCode(). Bunu nasıl yapabilirim?




Bu soru 19 Temmuz 2010 saat 16: 27'de sizinkinden yaklaşık 3 ay önce sorulmuştu. Ancak, yinelenen hedef olması gereken her zaman en eski gönderi değildir. Görüş sayısı, oy sayısı, cevap sayısı ve sorunun netliği dikkate alınmalıdır. Bunu kopya olarak işaretleyerek, insanların sorunuzu cevaplayan diğer cevapları bulmalarına yardımcı olabilir.
Suragch


Perde arkasında ne olduğunu gerçekten anlamak için, her zaman bunun gibi derinlemesine bir makale okumanızı öneririm: medium.com/androiddevelopers/…
Michal Vician

Yanıtlar:


175

Spannable daha esnektir:

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
Bu cevap için teşekkürler! Bu daha çok iOS'taki NSAttributedString'e benziyor :) Daha da esnek olmak için text.lenght'ı text2.indexOf (CepVizyon.getPhoneCode ()) ile değiştirerek String'in ilk bölümünü bilmemenizi sağlar.
iGranDav

1
Bir alan değil, bir yöntem olduğu gibi ()sonrasına koymalısınız . Kendim yapardım ama düzenlemeler en az 6 karakterden oluşmalıdır :)text.lengthlength
MSX

1
Spannable ile ilgili sorun, ellipsize = end'in artık çalışmamasıdır. Bu, bazı durumlarda oldukça ciddi bir sorundur.
Juan Carlos Ospina Gonzalez

1
Gayet iyi çalışıyor. Yine de bir HTML dizesi oluşturulması tavsiye edilir. Ve sonra HTML sınıfıyla ayrıştırıyoruz. Html.fromHtml(R.id.your_html_string);
sud007

Bu, alt çizginin rengini değiştirir (metin görünümünün altı çizili bayrağı varsa), bunu önlemek için herhangi bir yol var mı?
2018

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

65

Renk gerektiren statik metniniz varsa, dizeler dosyası aracılığıyla herhangi bir kod olmadan ekleyebilirsiniz:

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

sonra

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

sonuç

görüntü açıklamasını buraya girin

bunun hangi api sürümlerinde çalıştığından emin değilim, ancak şimdiye kadar test ettiğim api 19 için çalışmıyor, bu yüzden muhtemelen yalnızca en yeni api sürümlerinden bazıları bunu destekliyor

düzenleme: @hairraisin yorumlarda belirtildiği gibi , yazı tipi rengi fgcoloryerine kullanmayı deneyin color, daha düşük api seviyeleri için çalışmalıdır, ancak emin olmak için daha fazla test gerekir


3
<font fgcolor=...API 15 ve API 25 kullanarak başarılı bir şekilde test ettim (özellikle 19'u test etmedim)
saç kuru

Metni programlı olarak ayarladığımda çalışmıyor. :(
Rohit Singh

Bu ideal bir çözüm değildir, çünkü çevirileri metin renkleriyle karıştırır.
Miloš Černilovský

16

Maneesh'in cevabına gelince, bu işe yarayacaktır, ancak renk özelliği için tırnak işaretlerini eklemeniz ve bunlardan kaçmanız gerekir.

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

8

Benim için iyi!

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

7

Burada SpannableString, bir dizenin bir kısmının rengini değiştirmek için kullanılan Kotlin'deki çözüm .

    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

6

İşte colorizeandyboot'un cevabına dayalı bir işlev:

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

Tüm uygulamalarımda çok yaptığım metnin kısımlarını renklendirmek istediğim her seferinde bunu kodla yapma fikrinden hoşlanmadım (ve bazı durumlarda metin çalışma zamanında farklı satır içi ile ayarlandığı için) tanımlanmış renkler) bu yüzden kendiminkini yarattım MarkableTextView.

Fikir şuydu:

  • XML etiketlerini dizeden algılama
  • Etiket adını tanımlayın ve eşleştirin
  • Metnin niteliklerini ve konumunu ayıklayın ve kaydedin
  • Etiketi kaldırın ve içeriği saklayın
  • Nitelikleri yineleyin ve stilleri uygulayın

İşte adım adım süreç:

Öncelikle, belirli bir dizedeki XML etiketlerini bulmanın bir yolunu bulmalıydım ve Regexhile yaptım ..

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

Yukarıdakinin bir XML etiketiyle eşleşmesi için aşağıdaki kriterlere sahip olması gerekir:

  • Gibi <a> <a > <a-a> <a ..attrs..>ama değil geçerli etiket adı< a> <1>
  • Eşleşen bir ada sahip olan <a></a>ancak olmayan kapanış etiketi<a></b>
  • "Hiçbir şey" stiline gerek olmadığından herhangi bir içerik

Şimdi öznitelikler için bunu kullanacağız ..

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

Aynı konsepte sahip ve genellikle her ikisi için de uzağa gitmeme gerek kalmadı çünkü herhangi bir şey formatın dışına çıkarsa derleyici geri kalanı halledecektir.

Şimdi çıkarılan verileri tutabilecek bir sınıfa ihtiyacımız var:

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

Her şeyden önce, eşleşmeler arasında dolaşmak için uzun süredir kullandığım bu harika yineleyiciyi ekleyeceğiz (yazarı hatırlayamıyorum) :

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

Son olarak, şekillendirme, işte bu cevap için yaptığım çok basit bir şekillendirici:

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

Ve Markabletanımları içeren sınıf şu şekilde görünür:

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

Şimdi ihtiyacımız olan tek şey bir dizgeye başvurmaktır ve temelde şöyle görünmelidir:

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

Bir etiketleri sarmak için emin olun CDATA Sectionve kaçış "ile \.

Bunu, gereksiz kodların arkasına doldurulmasına gerek kalmadan metnin bölümlerini farklı şekillerde işlemek için modüler bir çözüm olarak yaptım.


4

Andy Boot'un dediği gibi yaptım, ancak benim de tıklanabilir bir aralığım vardı ve sipariş setSpansçağrıldığı için işe yaramadı . Bu nedenle , TextView'da rengi elde etmek için önce çağırmanız ve spannable.setSpan(clickableSpanand...ardındanspannable.setSpan(new ForegroundColorSpan...


4

Bu küçük işlevi yaptım, sadece metninizi renge, o metnin rengini renklendirmek istediğiniz şeyin başlangıç ​​ve bitiş dizinlerini ve rengin kendisini aktarın

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
        }

Kullanım

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

2

Genel amaçlı bir Kotlin eklenti işlevi ile şöyle görünür:

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

1

Karakter çıkışları + Html.fromHtml () kullanın

görüntü açıklamasını buraya girin

Dize, dize kaynak klasöründe nasıl saklanır

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

TextView'da nasıl gösterilir?

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

Bonus:

Çıktıdaki String şuna benzer

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

1

Alejandro H. Cruz'un yukarıdaki cevabından esinlenilmiştir .

İşlevi yalnızca tek bir alt dize eşleşmesi için çalışıyor, yöntemini Regex'i kullanacak şekilde güncelledim ve tüm eşleşmelerde renkleri güncellemeliyim:

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)

    }
}

Büyük .toRegex(RegexOption.IGNORE_CASE)/ küçük harfe bakılmaksızın dizeyi eşleştirmek istiyorsanız kullanın.
JustinMorris

0

A'dan çağrılabilen bu küçük yardımcı yöntemi yarattım 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
}

Kullanmak için:

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'da test edildi, teşekkürler!


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

-5

Bunun bir yolu , bir tanesi yalnızca telefon kodu için olacak olan myTextViewbirkaç ayrı parçaya TextViewsbölmektir. O zaman bu özelliğin rengini kontrol etmek TextViewoldukça basittir.


7
Hayır, baş belası. Spannable kullanmak doğru yoldur.
Marc DiMillo

Spannable sınıfı bunu bölme olmadan yapabilir
Sz-Nika Janos
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.