Highlight searched text in ListView items
Asked Answered
K

3

16

enter image description here

I have a ListView and i am using a custom adapter to show data. Now i want to change searched text letter colour as in above screen shot.

Here is the code for SearchView

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.actionbar_menu_item, menu);
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    final SearchView searchView = (SearchView) menu.findItem(R.id.action_search)
            .getActionView();
    searchView.setSearchableInfo(searchManager
            .getSearchableInfo(getComponentName()));
    searchView.setOnQueryTextListener(this);
        return super.onCreateOptionsMenu(menu);
        }

public boolean onQueryTextChange(String newText) {
    // this is adapter that will be filtered
      if (TextUtils.isEmpty(newText)){
            lvCustomList.clearTextFilter();
      }
      else{
            lvCustomList.setFilterText(newText.toString());
       }
    return false;
 }

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

Thank you.

Krawczyk answered 28/5, 2014 at 7:14 Comment(4)
Spannable String is your way to go.Mouthful
@AdilSoomro i want bold or changed color looks like above screen shot image (look above)Krawczyk
Spannable String will cover that as well.Mouthful
i know Spannable String is used but i dont know how to implement in my caseKrawczyk
B
30

I assume that you have a custom Adapter with getCount() and getView() implemented and already filtering items, and you just need the bold part.

To achieve that, you need to use a SpannableString, which is basically text with markup attached. For example, a TextAppearanceSpan can be used to change typeface, font style, size, and color.

So, you should update your adapter's getView() to change the part where you use textView.setText() into something more or less like this:

String filter = ...;
String itemValue = ...;

int startPos = itemValue.toLowerCase(Locale.US).indexOf(filter.toLowerCase(Locale.US));
int endPos = startPos + filter.length();

if (startPos != -1) // This should always be true, just a sanity check
{
    Spannable spannable = new SpannableString(itemValue);
    ColorStateList blueColor = new ColorStateList(new int[][] { new int[] {}}, new int[] { Color.BLUE });
    TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);

    spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setText(spannable);
}
else
    textView.setText(itemValue);
Brow answered 31/5, 2014 at 7:25 Comment(3)
what does "filter.length()" do with endPos?Terza
@Terza It's just calculating the starting and ending positions of the substring within the larger string.Brow
Ok. So, for example one can highlight a portion of the larger string? Also, does the position index start at 0 or 1?Terza
M
2

Android Search Highlight Example [Case Insensitive Order]

1. Search: [Highlight Specific Word]

public static SpannableStringBuilder highlightSearchText(SpannableStringBuilder fullText, String searchText) {

    if (searchText.length() == 0) return fullText;

    SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
    Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher(fullText);
    while (m.find()) {

        int wordStart = m.start();
        int wordEnd = m.end();

        setWordSpan(wordSpan, wordStart, wordEnd);

    }

    return wordSpan;
}

2. Search: [Highlight Full Word]

public static SpannableStringBuilder highlightSearchText(SpannableStringBuilder fullText, String searchText) {

    if (searchText.length() == 0) return fullText;

    final String searchBoundary = " \n()।.,;?-+!";
    char[] boundaries = searchBoundary.toCharArray();

    // highlight search text
    if (isNotEquals(searchText, boundaries)) {

        SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
        Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(fullText);
        while (m.find()) {

            int wordStart = m.start();
            while (wordStart >= 0 && isNotEquals(fullText.charAt(wordStart), boundaries)) {
                --wordStart;
            }
            wordStart = wordStart + 1;

            int wordEnd = m.end();
            while (wordEnd < fullText.length() && isNotEquals(fullText.charAt(wordEnd), boundaries)) {
                ++wordEnd;
            }

            setWordSpan(wordSpan, wordStart, wordEnd);

        }

        return wordSpan;

    } else {
        return fullText;
    }
}

private static boolean isNotEquals(String searchText, char[] boundaries) {
    for (char boundary : boundaries) {
        boolean equals = searchText.equals(String.valueOf(boundary));
        if (equals) return false;
    }
    return true;
}

private static boolean isNotEquals(char charAt, char[] boundaries) {
    for (char boundary : boundaries) {
        boolean isEquals = charAt == boundary;
        if (isEquals) return false;
    }
    return true;
}

Common Method:

private static void setWordSpan(SpannableStringBuilder wordSpan, int wordStart, int wordEnd) {
    // Now highlight based on the word boundaries
    ColorStateList redColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{0xffa10901});
    TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, redColor, null);

    wordSpan.setSpan(highlightSpan, wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    wordSpan.setSpan(new BackgroundColorSpan(0xFFFCFF48), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    wordSpan.setSpan(new RelativeSizeSpan(1.25f), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
Marquet answered 12/12, 2017 at 12:3 Comment(3)
Thanks for your sharing.!! It save me all day.Retread
Why should we need searchText = searchText.replace("'", "");Retread
@Retread How to call highlightSearchText ? I have a similar question. Can u help me out with it? #60930895Campion
P
0

You can use the CodeView library to implement this feature, all you need is to create 2 variables in the adapter to store the pattern of search result and the color of highlighting,

private Pattern syntaxPattern;
private Color highlightColor = Color.MAGENTA;

and create a setter in the adapter for pattern and color

public void updateSyntaxPattern(Pattern pattern) {
    syntaxPattern = pattern;
    notifyDataSetChanged();
}

then in getView method for ArrayAdapter or ViewHolder in RecyclerAdapter you need to add this pattern to the CodeView instance and remove the old patterns (from the last search result)

if(syntaxPattern != null) {
    codeView.resetSyntaxPatternList();
    codeView.addSyntaxPattern(syntaxPattern, highlightColor);
}

Now in SearchView onQueryTextSubmit or onQueryTextChange, depend on when you want the highlighter to work you will choose one of them you need to make the pattern from the search result and set it to the adapter

Pattern pattern = Pattern.compile(query);
adapter.updateSyntaxPattern(pattern);

It will work exactly as you want

enter image description here

Player answered 5/6, 2021 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.