TextView의 일부 색상을 어떻게 변경할 수 있습니까?


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

CepVizyon.getPhoneCode()의 문자열 색상을 변경하고 싶습니다 . 어떻게 할 수 있습니까?




그 질문은 당신의 질문보다 약 3 개월 전인 '10 년 7 월 19 일 16:27에 질문되었습니다. 그러나 중복 대상이되어야하는 것이 항상 가장 오래된 게시물은 아닙니다. 조회 수, 투표 수, 답변 수 및 질문의 명확성을 고려해야합니다. 이것을 중복으로 표시하면 사람들이 귀하의 질문에 답하는 다른 답변을 찾는 데 도움이 될 수 있습니다.
Suragch


배후에 무엇이 있는지 이해하기 위해 항상 다음과 같은 심층 기사를 읽어 볼 것을 제안합니다. medium.com/androiddevelopers/…
Michal Vician

답변:


170

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

3
이 답변에 감사드립니다! 이것은 iOS의 NSAttributedString과 비슷합니다. 더 유연하게하려면 text.lenght를 text2.indexOf (CepVizyon.getPhoneCode ())로 바꾸면 문자열의 첫 부분을 알 수 없습니다.
iGranDav

1
당신은 넣어해야 ()text.length같은 length방법이 아닌 필드입니다. 그것을 자신 할 것입니다하지만 편집은 : 적어도 6 자이어야합니다
MSX

이것은 지금까지 최고의 대답입니다.
파우 Arlandis 마르티네즈

1
Spannable의 문제는 ellipsize = end가 더 이상 작동하지 않는다는 것입니다. 어떤 경우에는 꽤 심각한 문제입니다.
Juan Carlos Ospina Gonzalez

1
잘 작동합니다. HTML 문자열을 만드는 것이 좋습니다. 그런 다음 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

색상이 필요한 정적 텍스트가있는 경우 문자열 파일을 통해 코드없이 추가 할 수 있습니다.

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

결과

여기에 이미지 설명 입력

이것이 어떤 API 버전에서 작동하는지 확실하지 않지만 지금까지 테스트 한 api 19에서는 작동하지 않으므로 아마도 최신 API 버전 중 일부만 이것을 지원할 것입니다.

편집 : @hairraisin이 주석에서 언급했듯이 글꼴 색상 fgcolor대신 사용 을 시도 color하십시오. 그러면 더 낮은 API 수준에서 작동하지만 확인하려면 더 많은 테스트가 필요합니다.


3
<font fgcolor=...API 15 및 API 25를 사용하여 성공적으로 테스트했습니다 (하지만 19를 구체적으로 테스트하지는 않았습니다)
hair raisin

프로그래밍 방식으로 텍스트를 설정할 때 작동하지 않습니다. :(
Rohit Singh

이것은 번역을 텍스트 색상과 혼합하기 때문에 이상적인 솔루션이 아닙니다.
Miloš Černilovský

16

Maneesh의 답변과 관련하여 이것은 작동하지만 color 속성에 대한 따옴표를 추가하고 이스케이프해야합니다.

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

8

나에게 좋다!

            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

SpannableString문자열 부분의 색상을 변경 하는 데 사용 하는 Kotlin의 솔루션입니다 .

    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
감사합니다. Kotlin을위한 간단하고 우아한 솔루션입니다.
Nhon Nguyen

5

다음 colorize은 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

나는 내 모든 앱에서 많이해온 텍스트의 일부에 색상을 지정하고 싶을 때마다 코드로이 작업을 수행한다는 아이디어가 마음에 들지 않았습니다. 정의 된 색상) 그래서 내가 직접 만들었습니다. 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>
  • "nothing"스타일을 지정할 필요가 없기 때문에 모든 콘텐츠

이제 속성에 대해 이걸 사용하겠습니다 ..

([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및 탈출 "\ .

필자는 불필요한 코드를 채울 필요없이 모든 다른 방식으로 텍스트의 일부를 처리하는 모듈 식 솔루션으로 만들었습니다.


4

나는 andy boot가 말한대로했지만 클릭 할 수있는 스팬도 있었고, 순서 setSpans가 호출 되었기 때문에 작동하지 않았습니다 . 당신이 먼저 전화를해야 그래서 spannable.setSpan(clickableSpanand...다음은 spannable.setSpan(new ForegroundColorSpan...텍스트 뷰의 색상을 얻을 수 있습니다


4

이 작은 기능을 만들었습니다. 텍스트를 색상으로 전달하고 해당 텍스트의 색상을 지정하려는 시작 및 끝 인덱스와 색상 자체를 전달합니다.

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
        }

용법

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

2

범용 Kotlin 확장 기능을 사용하면 다음과 같습니다.

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

문자 이스케이프 + Html.fromHtml () 사용

여기에 이미지 설명 입력

문자열 리소스 폴더에 문자열을 저장하는 방법

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

TextView에 표시하는 방법은 무엇입니까?

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

보너스:

출력의 문자열은 다음과 같습니다.

<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

한 가지 방법은 myTextView몇 개의 개별 으로 분할 TextViews하는 것입니다. 그중 하나는 전화 코드 전용입니다. 그러면이 특정 색상을 제어하는 TextView것은 매우 간단합니다.


7
아니, 엉덩이에 통증. 스패너 블을 사용하는 것이 올바른 방법입니다.
Marc DiMillo

Spannable 클래스는 할 수있는 분할없이
SZ - 니카 야노스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.