用于部分词搜索的 Searchview
Searchview for partial word search
我想在 Android 中实现 SearchView
以进行部分词搜索。
我做了如下搜索机制,如何获取部分词搜索功能?
例如。如果我搜索 "stackover..",Whosebug 会出现,但如果我搜索 "tackover..",Whosebug 不会出现,我需要它来搜索单词中的部分匹配项。
package jagranerp.myapplication;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
public class MainActivity extends Activity {
// List view
private ListView lv;
// Listview Adapter
ArrayAdapter<String> adapter;
// Search EditText
EditText inputSearch;
// ArrayList for Listview
ArrayList<HashMap<String, String>> productList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Listview Data
String products[] = {"Dell Inspiron", "HTC One X", "HTC Wildfire S", "HTC Sense", "HTC Sensation XE",
"iPhone 4S", "Samsung Galaxy Note 800",
"Samsung Galaxy S3", "MacBook Air", "Mac Mini", "MacBook Pro"};
lv = (ListView) findViewById(R.id.list_view);
inputSearch = (EditText) findViewById(R.id.inputSearch);
// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name, products);
lv.setAdapter(adapter);
/**
* Enabling Search Filter
* */
inputSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
MainActivity.this.adapter.getFilter().filter(cs);
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
@Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
}
}
filter() 方法将return关键字的巧合"cs"。在此示例中,"Whosebug" 将不匹配 "tackover",因为如果 "Whosebug" 以 "Tackover" 开头,比较将计算。因为它将 return 为假,所以它不会通过过滤器。
我不太确定过滤器是如何工作的,但您可以使用字符串 class 的方法 contains(String arg)。如果找到给定参数的任何匹配项,此方法将 return 为真。
String mySearch = "tackoverflow";
String match1 = "Whosebug";
if (match1.contains(mySearhch)){
//It contains the match
}
希望对你有帮助
我自己有兴趣做一些与此要求类似的事情,所以我尝试了一些东西。因此,这段代码不仅仅是一个简短的片段/快速修复。
我创建了自己的 CustomArrayAdapter
,它扩展了 BaseAdapter
,主要基于 ArrayAdapter 的源代码。我还没有实现在适配器列表中添加、删除或修改项目的方法,但这些方法可以很容易地从源代码中复制/改编(注意这些方法使用 synchronize
使适配器线程安全 - 请务必遵循模型)。
关键是创建你自己的 Filter
,它在 ArrayAdapter
中是一个内部私有 class,所以它不仅仅是直接扩展 ArrayAdapter
的简单情况。
chntgomez 的回答指向了正确的方向 - ArrayAdapter
的 Filter
只是使用 startsWith(...)
来匹配约束。它首先在完整字符串的开头尝试它,然后尝试拆分字符串(使用 space 字符作为分隔符)以检查多词字符串是否 startWith(...)
约束(前缀)。
通过将 startsWith(...)
的使用更改为 contains(...)
,您可以在单个字符或字符序列上实现 'match'。拆分任何多词字符串的代码也可以删除,因为它不是必需的。
以下 CustomArrayAdapter
及其 Filter
与原始问题中发布的 Activity
一起使用(显然将 ArrayAdapter
改为 CustomArrayAdapter
)。
public class CustomArrayAdapter<T> extends BaseAdapter implements Filterable {
private List<T> mObjects;
private final Object mLock = new Object();
private ArrayList<T> mOriginalValues;
private Filter mFilter;
private int mResource;
private int mDropDownResource;
private int mFieldId = 0;
private boolean mNotifyOnChange = true;
private Context mContext;
private LayoutInflater mInflater;
public CustomArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) {
init(context, resource, textViewResourceId, Arrays.asList(objects));
}
private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
mContext = context;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResource = mDropDownResource = resource;
mObjects = objects;
mFieldId = textViewResourceId;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mNotifyOnChange = true;
}
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new CustomArrayFilter();
}
return mFilter;
}
@Override
public int getCount() {
return mObjects.size();
}
@Override
public T getItem(int position) {
return mObjects.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("CustomArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"CustomArrayAdapter requires the resource ID to be a TextView", e);
}
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
}
return view;
}
private class CustomArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence matchChars) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (matchChars == null || matchChars.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String matchString = matchChars.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
if (valueText.contains(matchString)) {
newValues.add(value);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
我想在 Android 中实现 SearchView
以进行部分词搜索。
我做了如下搜索机制,如何获取部分词搜索功能?
例如。如果我搜索 "stackover..",Whosebug 会出现,但如果我搜索 "tackover..",Whosebug 不会出现,我需要它来搜索单词中的部分匹配项。
package jagranerp.myapplication;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
public class MainActivity extends Activity {
// List view
private ListView lv;
// Listview Adapter
ArrayAdapter<String> adapter;
// Search EditText
EditText inputSearch;
// ArrayList for Listview
ArrayList<HashMap<String, String>> productList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Listview Data
String products[] = {"Dell Inspiron", "HTC One X", "HTC Wildfire S", "HTC Sense", "HTC Sensation XE",
"iPhone 4S", "Samsung Galaxy Note 800",
"Samsung Galaxy S3", "MacBook Air", "Mac Mini", "MacBook Pro"};
lv = (ListView) findViewById(R.id.list_view);
inputSearch = (EditText) findViewById(R.id.inputSearch);
// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name, products);
lv.setAdapter(adapter);
/**
* Enabling Search Filter
* */
inputSearch.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
MainActivity.this.adapter.getFilter().filter(cs);
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
@Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
}
}
filter() 方法将return关键字的巧合"cs"。在此示例中,"Whosebug" 将不匹配 "tackover",因为如果 "Whosebug" 以 "Tackover" 开头,比较将计算。因为它将 return 为假,所以它不会通过过滤器。
我不太确定过滤器是如何工作的,但您可以使用字符串 class 的方法 contains(String arg)。如果找到给定参数的任何匹配项,此方法将 return 为真。
String mySearch = "tackoverflow";
String match1 = "Whosebug";
if (match1.contains(mySearhch)){
//It contains the match
}
希望对你有帮助
我自己有兴趣做一些与此要求类似的事情,所以我尝试了一些东西。因此,这段代码不仅仅是一个简短的片段/快速修复。
我创建了自己的 CustomArrayAdapter
,它扩展了 BaseAdapter
,主要基于 ArrayAdapter 的源代码。我还没有实现在适配器列表中添加、删除或修改项目的方法,但这些方法可以很容易地从源代码中复制/改编(注意这些方法使用 synchronize
使适配器线程安全 - 请务必遵循模型)。
关键是创建你自己的 Filter
,它在 ArrayAdapter
中是一个内部私有 class,所以它不仅仅是直接扩展 ArrayAdapter
的简单情况。
chntgomez 的回答指向了正确的方向 - ArrayAdapter
的 Filter
只是使用 startsWith(...)
来匹配约束。它首先在完整字符串的开头尝试它,然后尝试拆分字符串(使用 space 字符作为分隔符)以检查多词字符串是否 startWith(...)
约束(前缀)。
通过将 startsWith(...)
的使用更改为 contains(...)
,您可以在单个字符或字符序列上实现 'match'。拆分任何多词字符串的代码也可以删除,因为它不是必需的。
以下 CustomArrayAdapter
及其 Filter
与原始问题中发布的 Activity
一起使用(显然将 ArrayAdapter
改为 CustomArrayAdapter
)。
public class CustomArrayAdapter<T> extends BaseAdapter implements Filterable {
private List<T> mObjects;
private final Object mLock = new Object();
private ArrayList<T> mOriginalValues;
private Filter mFilter;
private int mResource;
private int mDropDownResource;
private int mFieldId = 0;
private boolean mNotifyOnChange = true;
private Context mContext;
private LayoutInflater mInflater;
public CustomArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) {
init(context, resource, textViewResourceId, Arrays.asList(objects));
}
private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
mContext = context;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResource = mDropDownResource = resource;
mObjects = objects;
mFieldId = textViewResourceId;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mNotifyOnChange = true;
}
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new CustomArrayFilter();
}
return mFilter;
}
@Override
public int getCount() {
return mObjects.size();
}
@Override
public T getItem(int position) {
return mObjects.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
if (mFieldId == 0) {
// If no custom field is assigned, assume the whole resource is a TextView
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
}
} catch (ClassCastException e) {
Log.e("CustomArrayAdapter", "You must supply a resource ID for a TextView");
throw new IllegalStateException(
"CustomArrayAdapter requires the resource ID to be a TextView", e);
}
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence)item);
} else {
text.setText(item.toString());
}
return view;
}
private class CustomArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence matchChars) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
if (matchChars == null || matchChars.length() == 0) {
ArrayList<T> list;
synchronized (mLock) {
list = new ArrayList<T>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
String matchString = matchChars.toString().toLowerCase();
ArrayList<T> values;
synchronized (mLock) {
values = new ArrayList<T>(mOriginalValues);
}
final int count = values.size();
final ArrayList<T> newValues = new ArrayList<T>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
final String valueText = value.toString().toLowerCase();
if (valueText.contains(matchString)) {
newValues.add(value);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}