13

I have a TextView with a random background color (could be any color really). I also have a text on this Textview that need to be readable. I assume the best solution is to highlight the said text in white and set the text color to black.

My question is: Is it possible to highlight the text inside a texview from the XML?

I have the following in my layout:

  <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/colorButton4"
        android:layout_gravity="right|bottom"
        android:background="@drawable/layout_border"
        android:layout_marginRight="30dp"
        android:layout_marginBottom ="30dp"
        android:clickable="true"
        android:onClick="onClick"
        android:gravity="center"
        android:textColorHighlight="@color/bgWhite"
        android:textColor="@color/Black"
        android:text="5431354" />

But it deosn't highlight the text.

Slamit
  • 465
  • 1
  • 6
  • 21
  • What do you mean with highlight? – finki Jul 21 '16 at 16:19
  • 2
    Have the text visible no matter the TextView background color. Like this: http://i1.wp.com/texblog.org/Wordpress/wp-content/uploads/2015/05/latex-highlight-text.png – Slamit Jul 21 '16 at 16:24
  • have a look at my answer https://stackoverflow.com/a/47142724/2685454 – Sumit Nov 06 '17 at 17:41

8 Answers8

18

You probably want to use a SpannableString for this, which allows individual parts of a string to be rendered differently in a TextView.

Like so:

    SpannableString str = new SpannableString("Highlighted. Not highlighted.");
    str.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, 11, 0);
    textView.setText(str);
Egg
  • 1,986
  • 16
  • 20
  • So you are telling me it's not possible to do without having it somewhere in my Java code? – Slamit Jul 21 '16 at 16:29
  • It's possible to style text in the strings.xml by using some particular HTML tags. Try one of these answers: http://stackoverflow.com/questions/17559019/text-view-with-different-colored-texts-in-xml-code but use background color instead of font color – Egg Jul 21 '16 at 16:33
  • Thanks for the link. It give some interesting points! Sadly, none of the XML only solutions work. I guess I'll have to use some java code for that. – Slamit Jul 21 '16 at 17:33
  • @Slamit Yeah I think, I also have to write some java code to highlight. – Moshi Apr 19 '17 at 07:08
  • follow this link : https://github.com/datanapps/HighlightedTextView – Yogendra Jun 26 '21 at 10:53
12

To highlight all occurrences of specific text use this method:

private void highlightString(String input) {
//Get the text from text view and create a spannable string
SpannableString spannableString = new SpannableString(mTextView.getText());
//Get the previous spans and remove them
BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);

for (BackgroundColorSpan span: backgroundSpans) {
    spannableString.removeSpan(span);
}

//Search for all occurrences of the keyword in the string
int indexOfKeyword = spannableString.toString().indexOf(input);

while (indexOfKeyword > 0) {
    //Create a background color span on the keyword
    spannableString.setSpan(new BackgroundColorSpan(Color.YELLOW), indexOfKeyword, indexOfKeyword + input.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

    //Get the next index of the keyword
    indexOfKeyword = spannableString.toString().indexOf(input, indexOfKeyword + input.length());
}

//Set the final text on TextView
mTextView.setText(spannableString);}

Note: mTextView is a TextView object in which you want to highlight your text

Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
12

Easy Way

You can use Spannable class for formatting Text.

textView.setText("Hello, I am Awesome, Most Awesome"); // set text first
setHighLightedText(textView, "a"); // highlight all `a` in TextView

Output will be something like below image.

Output

Here is the method.

 /**
     * use this method to highlight a text in TextView
     *
     * @param tv              TextView or Edittext or Button (or derived from TextView)
     * @param textToHighlight Text to highlight
     */
    public void setHighLightedText(TextView tv, String textToHighlight) {
        String tvt = tv.getText().toString();
        int ofe = tvt.indexOf(textToHighlight, 0);
        Spannable wordToSpan = new SpannableString(tv.getText());
        for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
            ofe = tvt.indexOf(textToHighlight, ofs);
            if (ofe == -1)
                break;
            else {
                // set color here
                wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + textToHighlight.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
            }
        }
    }

You can check this answer for clickable highlighted text.

Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
  • Thank you for suggesting this Khemraj, The question was quite restrictive as I needed to do this from the XML (so not through Java). What you offer would not help sadly! I assumed that it was not possible and reworked my layout as an alternative solution! – Slamit Dec 02 '18 at 18:14
  • You should put a better name than "ofe". Also, you don't need to call "setText" inside the loop. It's enough to set it outside of it. – android developer Aug 18 '19 at 13:19
  • I'm also not sure if you need the parameter of `TextView.BufferType.SPANNABLE` . It works for me without it. Why did you put it? Maybe doesn't work on old Android versions? – android developer Aug 18 '19 at 14:33
  • 1
    how would I make the match case insensitive? – JerseyDevel Jul 19 '20 at 16:46
  • This answer causes the app to hang when processing / displaying more than 5,000 characters. – JerseyDevel Sep 16 '20 at 21:17
  • how can I make the Highlight on text without consideration if it in lower or upper case? – Amin May 30 '21 at 13:30
  • best answer! just need to change the first line in case your word starts with uppercase letter instead of `String tvt = tv.getText().toString();` use `String tvt = tv.getText().toString().toLowerCase();` – J El Sep 22 '21 at 23:44
2

I've wrote a Kotlin method that will highlight all the keywords in all occurrences in the String and will return SpannableString.

fun main() {
    textView.text = highlightKeywords(
        highlightColor = ContextCompat.getColor(context, R.color.colorAccent),
        message = "Hello World, and Hello to all my Hello Friends.",
        keywords = listOf("Hello")
    )
}


fun highlightKeywords(
    highlightColor: Int,
    message: String,
    keywords: List<String>,
): SpannableString {
    val spannableString = SpannableString(message)
    keywords.forEach { keyword ->
        if (!keyword.isBlank()) {
            var startIndex = message.indexOf(keyword)

            while (startIndex >= 0) {
                spannableString.setSpan(
                    ForegroundColorSpan(highlightColor),
                    startIndex,
                    startIndex + keyword.length,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                )

                startIndex = message.indexOf(keyword, startIndex + keyword.length)
            }
        }
    }
    return spannableString
}
Morgan Koh
  • 2,297
  • 24
  • 24
  • This is a very old question and it was about using an XML only solution. thank you for sharing this though! – Slamit Nov 26 '20 at 11:40
  • Well, technically you can still use DataBinding method in the XML to trigger the Kotlin Extension method if you'd like. – Morgan Koh Nov 27 '20 at 02:20
1

After 4 years of working on Android I can safely say it is not possible to do without code outside of XML. The solutions shared by others will help you if you can use some code so have a read!

Edit: I selected a new answer as valid but it require to code a custom TextView!

Slamit
  • 465
  • 1
  • 6
  • 21
1

Suraj's answer is great and works, but has 2 missing components. First, it will not highlight the first word (as Rany commented) and second, it does not ignore case so searching for "test" in this String: "This is a Test" will not find anything.

This is my updated answer which resolves both of those via a passed argument and also adds in alpha in case you want to use a custom color for your highlighting. Note that the overloaded first method is a sample for how to return what the previous method did other than the first item being selected.

    
    /**
     * Use this method to get the same return as the previous method
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight){
        return buildHighlightString(originalText, textToHighlight, false, Color.YELLOW, 1.0F);
    }
    
    /**
     * Build a spannable String for use in highlighting text colors
     * 
     * @param originalText The original text that is being highlighted
     * @param textToHighlight The text / query that determines what to highlight
     * @param ignoreCase Whether or not to ignore case. If true, will ignore and "test" will have
     *                   the same return as "TEST". If false, will return an item as highlighted
     *                   only if it matches it case specficic.
     * @param highlightColor The highlight color to use. IE {@link Color#YELLOW} || {@link Color#BLUE}
     * @param colorAlpha Alpha to adjust how transparent the color is. 1.0 means it looks exactly
     *                   as it should normally where as 0.0 means it is completely transparent and
     *                   see-through. 0.5 means it is 50% transparent. Useful for darker colors
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight,
                                                       boolean ignoreCase, @ColorInt int highlightColor,
                                                       @FloatRange(from = 0.0, to = 1.0) float colorAlpha){
        SpannableString spannableString = new SpannableString(originalText);
        if (TextUtils.isEmpty(originalText) || TextUtils.isEmpty(textToHighlight)) {
            return spannableString;
        }
        String lowercaseOriginalString = originalText.toLowerCase();
        String lowercaseTextToHighlight = textToHighlight.toLowerCase();
        if(colorAlpha < 1){
            highlightColor = ColorUtils.setAlphaComponent(highlightColor, ((int)(255*colorAlpha)));
        }
        //Get the previous spans and remove them
        BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);
        for (BackgroundColorSpan span: backgroundSpans) {
            spannableString.removeSpan(span);
        }
        //Search for all occurrences of the keyword in the string
        int indexOfKeyword = (ignoreCase)
                ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight)
                : originalText.indexOf(textToHighlight);
        while (indexOfKeyword != -1) {
            //Create a background color span on the keyword
            spannableString.setSpan(new BackgroundColorSpan(highlightColor), indexOfKeyword,
                    indexOfKeyword + (textToHighlight.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            
            //Get the next index of the keyword
            indexOfKeyword = (ignoreCase)
                    ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight, (indexOfKeyword) + textToHighlight.length())
                    : originalText.indexOf(textToHighlight, (indexOfKeyword) + textToHighlight.length());
        }
        return spannableString;
    }
    
PGMacDesign
  • 6,092
  • 8
  • 41
  • 78
1

https://github.com/datanapps/HighlightedTextView

<datanapps.highlightedtextview.HighLightTextView
    android:id="@+id/tv2"
    android:layout_below="@+id/tv1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android is an open source and Linux-based operating system for mobile devices such as smartphones and tablet computers."
    android:textColor="@color/white"
    app:fontFamily="serif"
    android:lineSpacingExtra="50sp"
    android:layout_marginTop="20dp"
    android:textSize="20sp"
    android:textAlignment="viewEnd"
    app:highLightColor="@color/blue"
    />

enter image description here

Yogendra
  • 4,817
  • 1
  • 28
  • 21
0

If you would have checked the docs for TextView you would have found out that android:textColorHighlight does not do what you want: https://developer.android.com/reference/android/widget/TextView.html#attr_android:textColorHighlight

It's only used when selecting text, e.g. in an EditText. You need to set the background of the TextView to "hightlight" it.

finki
  • 2,063
  • 1
  • 20
  • 23
  • I understand that indeed. My question is is it possible by any other way? I'm assuming no but as a noob I ask in case there is a trick I do not know. – Slamit Jul 21 '16 at 16:30
  • I don't understand why you set a background in an textview when you want to "highlight" it with black anyway? ;) – finki Jul 21 '16 at 16:31
  • I want to have a TextView with a random background color and the text saying "the background color is " Now is the background color is with I can read my text, if it's navy it's hard to read, if it's back I can't read it. I hope it explain it properly – Slamit Jul 21 '16 at 16:34