我有一个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();
}
}
};
}
答案 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
。您的实际代码有效,但会检查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));
}
答案 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);
}
}