在ListView中突出显示搜索结果

时间:2013-06-01 12:55:56

标签: android string listview

我有一个ListView String秒。使用下面的代码我可以突出显示搜索结果,但用户必须键入单词以搜索区分大小写。 如何实现搜索结果的非区分大小写突出显示,例如本机Android联系人搜索?

这是我突出显示的代码。我扩展ArrayAdapter并实现自定义过滤器以获取要搜索的字符串。在getView方法中,我检查ListView中的String是否包含prefixString并突出显示它。

public class HighlightListAdapter extends ArrayAdapter {
    ArrayList<String> objects;
    final Object mLock =new Object();
    private ArrayList<String> mOriginalValues;
    private ArrayFilter filter;
    private String prefixString;
    public AuthorsListAdapter(Context context, int textViewResourceId,  ArrayList<String> objects) {
        super(context, textViewResourceId, objects);
        this.objects = objects;
    }



    class ViewHolder{
        TextView author;

    }

    public View getView(final int position, View convertView, ViewGroup parent){

        // assign the view we are converting to a local variable
        View v = convertView;
        ViewHolder holder = null;

        // first check to see if the view is null. if so, we have to inflate it.
        // to inflate it basically means to render, or show, the view.
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (v == null) {
            holder = new ViewHolder();
            v = inflater.inflate(R.layout.author_list_item, null);
            holder.author =(TextView) v.findViewById(R.id.author_list_item_text);
            v.setTag(holder);

        }else{
            holder = (ViewHolder) v.getTag();

        }


         final String author = objects.get(position);        
        if (author != null) {


        holder.author.setText(author);
        if(prefixString !=null && prefixString.length()>1){
            String s =  author;



        **if(s.contains(prefixString)){
            String rep = s.replace(prefixString,    "<b><font color=#2825A6>"+ prefixString+ "</font></b>");
            holder.author.setText(Html.fromHtml(rep));
        }** // higlight 



        }

            }

        return v;
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return objects.size();
    }



     @Override
    public Filter getFilter() {
        // TODO Auto-generated method stub
        if(filter == null){
            filter =new ArrayFilter();
        }
        return filter;
    }



    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return this.objects.get(position);
    }



    private class ArrayFilter extends Filter {
            @Override
            protected FilterResults performFiltering(CharSequence prefix) {
                FilterResults results = new FilterResults();

                if (mOriginalValues == null) {
                    synchronized (mLock) {
                        mOriginalValues = new ArrayList<String>(objects);
                    }
                }

                if (prefix == null || prefix.length() == 0) {
                    ArrayList<String> list;
                    synchronized (mLock) {
                        list = new ArrayList<String>(mOriginalValues);
                    }
                    results.values = list;
                    results.count = list.size();
                } else {
                    **prefixString = prefix.toString();** // get string to search

                    ArrayList<String> values;
                    synchronized (mLock) {
                        values = new ArrayList<String>(mOriginalValues);
                    }

                    final int count = values.size();
                    final ArrayList<String> newValues = new ArrayList<String>();

                    for (int i = 0; i < count; i++) {
                        final String value = values.get(i);
                        final String valueText = value.toString().toLowerCase();

                        // First match against the whole, non-splitted value
                        if (valueText.startsWith(prefixString)) {
                            newValues.add(value);
                        } else {
                            final String[] words = valueText.split(" ");
                            final int wordCount = words.length;

                            // Start at index 0, in case valueText starts with space(s)
                            for (int k = 0; k < wordCount; k++) {
                                if (words[k].startsWith(prefixString)) {
                                    newValues.add(value);
                                    break;
                                }
                            }
                        }
                    }

                    results.values = newValues;
                    results.count = newValues.size();
                }

                return results;
            }
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,  FilterResults results) {
                   objects = (ArrayList<String>) results.values;
                if (results.count > 0) {
                                   notifyDataSetChanged();
                               } else {
                                 notifyDataSetInvalidated();
                              }

            }

        };
    }

4 个答案:

答案 0 :(得分:29)

我使用的是:

  • 每次出现都会被替换(不仅是前缀)
  • 搜索时会忽略大小写和重音,但会保留在结果中。
  • 它直接使用SpannableString,您可以在setText()中使用。我相信它比使用中间html步骤更有效。

public static CharSequence highlight(String search, String originalText) {
    // ignore case and accents
    // the same thing should have been done for the search text
    String normalizedText = Normalizer.normalize(originalText, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "").toLowerCase();

    int start = normalizedText.indexOf(search);
    if (start < 0) {
        // not found, nothing to to
        return originalText;
    } else {
        // highlight each appearance in the original text
        // while searching in normalized text
        Spannable highlighted = new SpannableString(originalText);
        while (start >= 0) {
            int spanStart = Math.min(start, originalText.length());
            int spanEnd = Math.min(start + search.length(), originalText.length());

            highlighted.setSpan(new BackgroundColorSpan(<background_color>), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            start = normalizedText.indexOf(search, spanEnd);
        }

        return highlighted;
    }
}

答案 1 :(得分:3)

接受的答案很好。但是你可以通过一行代码完成。我在案件中为避免区分大小写的问题所做的是:

Spannable sb = new SpannableString(originalText);
                    sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), originalText.toLowerCase().indexOf(query.toLowerCase()),
                            originalText.toLowerCase().indexOf(query.toLowerCase()) + query.length(),
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
result.setText(sb);

希望它可能有所帮助! 注意:这里&#39;查询&#39;是要突出显示的字符串的一部分。

答案 2 :(得分:2)

首先,你的代码

if(s.contains(prefixString)){
    String rep = s.replace(prefixString,    "<b><font color=#2825A6>"+ prefixString+ "</font></b>");
    holder.author.setText(Html.fromHtml(rep));
}

不好。您应该使用String.startsWith来检查s的开头是否等于prefixString。您的实际代码有效,但会检查prefixStrings的存在,但不关心其位置。 对于不区分大小写的搜索,您可以在检查String.toLowerCase的存在时对两个字符串使用String.toUpperCaseprefixString。案例将被忽略。

if(s.toLowerCase().startsWith(prefixString.toLowerCase())){
    String rep = "<b><font color=#2825A6>" + prefixString + "</font></b>" + s.substring(prefixString.length());
    holder.author.setText(Html.fromHtml(rep));
}

答案 3 :(得分:1)

高级搜索突出显示示例[不区分大小写的顺序]

1。 简单搜索(Html):

public static void setSearchTextHighlightSimpleHtml(TextView textView, String fullText, String searchText) {
    searchText = searchText.replace("'", "");
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fullText = fullText.replaceAll("(?i)(" + searchText + ")", "<span style=\"background-color:#FCFF48;\"><b><big><font color='#a10901'>$1</font></big></b></span>");
            textView.setText(Html.fromHtml(fullText, Html.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE);
        } else {
            fullText = fullText.replaceAll("(?i)(" + searchText + ")", "<b><big><font color='red'>$1</font></big></b>");
            textView.setText(Html.fromHtml(fullText), TextView.BufferType.SPANNABLE);
        }
    } catch (Exception e) {
        textView.setText(fullText);
    }
}

2。 简单搜索(可跨越):

public static void setSearchTextHighlightSimpleSpannable(TextView textView, String fullText, String searchText) {

    searchText = searchText.replace("'", "");

    // highlight search text
    if (null != searchText && !searchText.isEmpty()) {

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

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

        }

        textView.setText(wordSpan, TextView.BufferType.SPANNABLE);

    } else {
        textView.setText(fullText);
    }
}

3。 快速搜索(高级):

public static void setAdvancedTitleHighlight(TextView textView, String fullText, String searchText) {

    searchText = searchText.replace("'", "");

    final String WORD_SINGLE = " ";

    // highlight search text
    if (null != searchText && !searchText.isEmpty() && !searchText.equals(WORD_SINGLE)) {

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

            final char WORD_BOUNDARY = ' ';

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

            int wordEnd = m.end();
            while (wordEnd < fullText.length() && fullText.charAt(wordEnd) != WORD_BOUNDARY) {
                ++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);

        }

        textView.setText(wordSpan, TextView.BufferType.SPANNABLE);

    } else {
        textView.setText(fullText);
    }
}

4. 详情搜索(高级):

public static void setAdvancedDetailsHighlight(TextView textView, String fullText, String searchText) {

    searchText = searchText.replace("'", "");

    final String WORD_SINGLE = " ";
    final String WORD_SINGLE1 = "\n";
    final String WORD_SINGLE2 = "(";
    final String WORD_SINGLE3 = ")";
    final String WORD_SINGLE4 = "।";
    final String WORD_SINGLE5 = ".";
    final String WORD_SINGLE6 = ",";
    final String WORD_SINGLE7 = ";";
    final String WORD_SINGLE8 = "?";
    final String WORD_SINGLE9 = "-";
    final String WORD_SINGLE10 = "+";

    // highlight search text
    if (null != searchText && !searchText.isEmpty() && !searchText.equals(WORD_SINGLE) && !searchText.equals(WORD_SINGLE1) && !searchText.equals(WORD_SINGLE2)
            && !searchText.equals(WORD_SINGLE3) && !searchText.equals(WORD_SINGLE4) && !searchText.equals(WORD_SINGLE5)
            && !searchText.equals(WORD_SINGLE6) && !searchText.equals(WORD_SINGLE7) && !searchText.equals(WORD_SINGLE8)
            && !searchText.equals(WORD_SINGLE9) && !searchText.equals(WORD_SINGLE10)) {

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

            final char WORD_BOUNDARY = ' ';
            final char WORD_BOUNDARY1 = '\n';
            final char WORD_BOUNDARY2 = '(';
            final char WORD_BOUNDARY3 = ')';
            final char WORD_BOUNDARY4 = '।';
            final char WORD_BOUNDARY5 = '.';
            final char WORD_BOUNDARY6 = ',';
            final char WORD_BOUNDARY7 = ';';
            final char WORD_BOUNDARY8 = '?';
            final char WORD_BOUNDARY9 = '-';

            int wordStart = m.start();
            while (wordStart >= 0 && fullText.charAt(wordStart) != WORD_BOUNDARY
                    && fullText.charAt(wordStart) != WORD_BOUNDARY1
                    && fullText.charAt(wordStart) != WORD_BOUNDARY2
                    && fullText.charAt(wordStart) != WORD_BOUNDARY3
                    && fullText.charAt(wordStart) != WORD_BOUNDARY4
                    && fullText.charAt(wordStart) != WORD_BOUNDARY5
                    && fullText.charAt(wordStart) != WORD_BOUNDARY6
                    && fullText.charAt(wordStart) != WORD_BOUNDARY7
                    && fullText.charAt(wordStart) != WORD_BOUNDARY8
                    && fullText.charAt(wordStart) != WORD_BOUNDARY9) {
                --wordStart;
            }
            wordStart = wordStart + 1;

            int wordEnd = m.end();
            while (wordEnd < fullText.length() && fullText.charAt(wordEnd) != WORD_BOUNDARY
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY1
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY2
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY3
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY4
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY5
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY6
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY7
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY8
                    && fullText.charAt(wordEnd) != WORD_BOUNDARY9) {
                ++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);

        }

        textView.setText(wordSpan, TextView.BufferType.SPANNABLE);

    } else {
        textView.setText(fullText);
    }
}