2013-06-01 3 views
6

У меня есть ListView с String s. В приведенном ниже коде я могу выделить результаты поиска, но пользователь должен ввести слова для поиска с учетом регистра. Как я могу реализовать нечеткую чувствительную подсветку результатов поиска, например, как собственный поиск контактов с Android?Выделить результаты поиска в ListView

Вот мой код для Подсветки. Я расширяю ArrayAdapter и реализую настраиваемый фильтр, чтобы получить строку для поиска. В методе getView я проверяю, содержит ли мой String в ListView 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(); 
           } 

      } 

     }; 
    } 
+0

Вы можете сделать как строчными, а затем сравнить их. Но ваш код работает неправильно. – user35443

+0

Если я делаю как строчные буквы, а сравниваю их, я должен сделать несколько букв в верхнем регистре, после того как я заменил его кодом HTML .. И это сложно .. Я могу застраховать вас, что этот код работает очень хорошо. У вас есть лучшее решение? – zennon

+0

Функция вызова функции экземпляра 'String' редко влияет на содержимое строки. Вы можете использовать 's.toLowerCase()', не изменяя его содержимое. Строка с нижней строкой возвращается. Проверить http://docs.oracle.com/javase/6/docs/api/java/lang/String.html – user35443

ответ

26

Это то, что я использую:

  • каждого вхождения заменяется (не только префикс)
  • Case и акцент игнорируются при поиске, но сохраняются в результате.
  • Он использует напрямую 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; 
    } 
} 
+0

Спасибо .. это приятное решение, чтобы выделить искомую строку в listViews! – zennon

+0

хорошая работа @bwt Я много искал для этого, и, наконец, я получил полезный код – Antarix

+1

Не могли бы вы рассказать, где именно использовать ваш метод для получения выделенного результата? Пытался вставить мой фильтр, но не повезло. Пожалуйста, помогите мне с этим. –

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. Ваш фактический код работает, но он проверяет наличие prefixString в s, но не заботится о его положении. Для поиска без учета регистра вы можете использовать String.toLowerCase или String.toUpperCase на обеих строках при проверке наличия prefixString. Случай будет проигнорирован.

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

С вашим кодом и с моим кодом тоже строчные строки в s никогда не будут заменены префиксString, если prefixString делает не содержат одинаковых символов в верхнем регистре. Я приведу вам пример: String s = «Johann Wolfgang von Goethe»; и String prefixString = "wolf"; В вашем коде и в моем коде тоже s.toLowerCase() содержит (prefixString.toLowerCase()) возвращает true, потому что «волк» находится в нижнем регистре s. Но метод s.replace не заменит prefixString. «Волк» не будет соответствовать «Волку». Надеюсь, теперь я лучше объяснил свою проблему. Спасибо за ваше время! – zennon

+0

Хорошо, ты прав. Но вам не нужно использовать замену. Посмотрите на мое редактирование. – user35443

+0

BTW мой код подсчитывается с 'prefixString', используемым как ** префикс **. – user35443

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

Надеюсь, это поможет! Примечание: здесь «запрос» - это часть строки, которую вы хотите выделить.

+0

Что такое запрос здесь? –

+0

Запрос - это часть строки, которую вы хотите выделить. –

+1

Превосходно, спасибо –

0

Расширенный поиск Highlight Пример [Case Нечувствительность Order]

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.Простой поиск (Spannable):

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.Быстрый поиск (Advanced):

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); 
    } 
} 
Смежные вопросы