Làm cách nào để thay đổi màu của một phần của TextView?


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

Tôi muốn thay đổi màu cho CepVizyon.getPhoneCode()chuỗi của. Tôi có thể làm cái này như thế nào?




Câu hỏi đó đã được đặt ra vào ngày 19 tháng 7 '10 lúc 16:27, khoảng 3 tháng trước câu hỏi của bạn. Tuy nhiên, không phải lúc nào bài đăng cũ nhất cũng cần phải là mục tiêu trùng lặp. Cần tính đến số lượt xem, số lượt bình chọn, số câu trả lời và độ rõ ràng của câu hỏi. Bằng cách đánh dấu điều này là trùng lặp, nó có thể giúp mọi người tìm thấy những câu trả lời khác cũng trả lời câu hỏi của bạn.
Suragch


Để thực sự hiểu những gì đằng sau hậu trường, tôi luôn khuyên bạn nên đọc một bài báo chuyên sâu như sau: medium.com/androiddevelopers/…
Michal Vician

Câu trả lời:


170

Spannable linh hoạt hơn:

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
Cảm ơn bạn vì câu trả lời này! Điều này giống như NSAttributedString trong iOS :) Để linh hoạt hơn nữa, hãy thay thế text.lenght bằng text2.indexOf (CepVizyon.getPhoneCode ()) cho phép bạn không biết phần đầu tiên của Chuỗi.
iGranDav

1
Bạn nên đặt ()sau text.lengthnhư lengthlà một phương thức không phải là một trường. Sẽ tự mình làm nhưng các chỉnh sửa phải có ít nhất 6 ký tự :)
MSX

Đây là câu trả lời tốt nhất cho đến nay.
Pau Arlandis Martinez

1
Vấn đề với Spannable là ellipsize = end không hoạt động nữa. Đó là một vấn đề khá nghiêm trọng trong một số trường hợp.
Juan Carlos Ospina Gonzalez

1
Hoạt động tốt. Mặc dù tạo một chuỗi HTML được khuyến khích. Và sau đó phân tích nó thông qua lớp 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

Nếu bạn có văn bản tĩnh cần màu sắc, bạn có thể thêm văn bản đó mà không cần bất kỳ mã nào qua tệp chuỗi:

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

sau đó

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

kết quả

nhập mô tả hình ảnh ở đây

không chắc phiên bản này hoạt động trên api nào, nhưng không hoạt động cho api 19 mà tôi đã thử nghiệm cho đến nay, vì vậy có lẽ chỉ một số phiên bản api mới nhất hỗ trợ điều này

chỉnh sửa: như @hairraisin đã đề cập trong các nhận xét, hãy thử sử dụng fgcolorthay vì colorcho màu phông chữ, sau đó nó sẽ hoạt động với các cấp api thấp hơn, nhưng cần thử nghiệm thêm để chắc chắn


3
Tôi đã thử nghiệm thành công sử dụng <font fgcolor=...trên API 15 và API 25 (Tôi không đặc biệt kiểm tra 19 mặc dù)
tóc nho khô

Không hoạt động khi tôi đặt văn bản theo chương trình. :(
Rohit Singh

Đây không phải là một giải pháp lý tưởng, vì nó trộn lẫn các bản dịch với màu văn bản.
Miloš Černilovský

16

Liên quan đến câu trả lời của Maneesh, điều này sẽ hiệu quả nhưng bạn cần thêm và thoát khỏi dấu ngoặc kép cho thuộc tính màu sắc.

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

8

Nó là tốt cho tôi!

            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

Đây là giải pháp trong Kotlin dùng SpannableStringđể thay đổi màu của một phần chuỗi.

    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
Cảm ơn bạn. Đơn giản là giải pháp thanh lịch cho Kotlin.
Nhon Nguyen

5

Đây là một colorizehàm dựa trên câu trả lời của 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

Tôi không thích ý tưởng làm điều này bằng mã mỗi khi tôi muốn tô màu các phần của văn bản mà tôi đã làm rất nhiều trong tất cả các ứng dụng của mình (và vì trong một số trường hợp, văn bản đang được đặt trong thời gian chạy với nội tuyến khác- màu xác định) vì vậy tôi đã tạo ra màu của riêng mình MarkableTextView.

Ý tưởng là:

  • Phát hiện các thẻ XML từ chuỗi
  • Xác định và đối sánh tên thẻ
  • Trích xuất và lưu các thuộc tính và vị trí của văn bản
  • Xóa thẻ và giữ lại nội dung
  • Lặp lại các thuộc tính và áp dụng các kiểu

Đây là quy trình từng bước:

Đầu tiên, tôi cần một cách để tìm các thẻ XML trong một chuỗi nhất định và Regexđã thực hiện thủ thuật ..

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

Để đối sánh ở trên với thẻ XML, nó phải có các tiêu chí sau:

  • Tên thẻ hợp lệ như <a> <a > <a-a> <a ..attrs..>nhưng không< a> <1>
  • Thẻ đóng có tên phù hợp như <a></a>nhưng không<a></b>
  • Bất kỳ nội dung nào, vì không cần phải tạo kiểu "không có gì"

Bây giờ đối với các thuộc tính, chúng tôi sẽ sử dụng cái này ..

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

Nó có cùng một khái niệm và nói chung tôi không cần phải đi xa cho cả hai vì trình biên dịch sẽ xử lý phần còn lại nếu bất cứ điều gì xảy ra ngoài định dạng.

Bây giờ chúng ta cần một lớp có thể chứa dữ liệu được trích xuất:

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

Trước bất kỳ điều gì khác, chúng tôi sẽ thêm trình lặp thú vị này mà tôi đã sử dụng từ lâu để lặp qua các trận đấu (không thể nhớ tác giả) :

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

Cuối cùng là tạo kiểu, vì vậy đây là một trình tạo kiểu rất đơn giản mà tôi đã tạo cho câu trả lời này:

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

Và đây là cách Markablelớp chứa các định nghĩa trông như thế nào:

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

Tất cả những gì chúng ta cần bây giờ là tham chiếu đến một chuỗi và về cơ bản nó sẽ giống như sau:

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

Đảm bảo bọc các thẻ bằng a CDATA Sectionvà thoát "bằng \.

Tôi đã thực hiện điều này như một giải pháp mô-đun để xử lý các phần của văn bản theo tất cả các cách khác nhau mà không cần phải nhồi các mã không cần thiết phía sau.


4

Tôi đã làm như andy boot nói, nhưng tôi cũng có khoảng thời gian có thể nhấp và nó không hoạt động vì thứ tự setSpansđược gọi. Vì vậy, trước tiên bạn phải gọi hàm spannable.setSpan(clickableSpanand...sau đó spannable.setSpan(new ForegroundColorSpan...để lấy màu trong TextView


4

Tôi đã thực hiện một chức năng nhỏ này, chỉ cần chuyển văn bản của bạn sang màu, chỉ mục bắt đầu và kết thúc của những gì bạn muốn tô màu cho văn bản đó và chính màu sắc

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
        }

Sử dụng

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

2

Với chức năng mở rộng Kotlin có mục đích chung, nó sẽ giống như sau:

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

Sử dụng ký tự thoát + Html.fromHtml ()

nhập mô tả hình ảnh ở đây

Cách lưu trữ Chuỗi trong thư mục tài nguyên chuỗi

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

Làm thế nào để hiển thị trong TextView?

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

Tặng kem:

Chuỗi trong đầu ra trông như thế này

<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

Một cách là chia myTextViewthành một vài mã riêng biệt TextViews, một trong số đó sẽ chỉ dành cho mã điện thoại. Sau đó, kiểm soát màu sắc của cụ thể TextViewnày là khá đơn giản.


7
Không, đau ở mông. Sử dụng một spannable là cách đúng đắn.
Marc DiMillo

Lớp Spannable thể làm điều đó mà không cần tách
Sz-Nika Janos
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.