AutoCompleteTextView 在做搜索功能时常常被应用到,它的好处是根据用户输入的信息实现后缀信息的提示功能。
此控件通常的用法是ArrayAdapteradapter = new ArrayAdapter (this,android.R.layout.simple_dropdown_item_1line, COUNTRIES); AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit); textView.setAdapter(adapter);
其中 adapter 可以任意构造, COUNTRIES 是数据源,如 COUNTTRIES=["aaaaa","bbbbbb","cccccc"], 那么当在框中输入a时,则AutoCompleteTextView 自动提示aaaaa,这种根据数据源的值来提示做起来很是简单,略过。
下面讲一下自定义匹配规则的提示,假如现在有这么一个需求,根据输入城市的拼音,来提示相对应的城市列表,这样就不能按上述方式来做了,因为你输入的信息并不是数据源的信息,所以你必须得自定义匹配规则来找出对应的城市提示,思路是这样的:先把所有的城市及对应的拼音放在一个Hashtable里,然后自己实现一个 Adapter 来加载这些数据源,当用户输入每一个拼音字符时,需实时监听到(系统主动监听),然后去Hashtable里找到对应的城市设置到Adapter里,这样在控件的下拉列表中就可以看到所需的城市列表提示,我贴一段 Adapter代码供大家参考一下,定义了一个SearchCityAdapter extends BaseAdapter implements Filterable,其中Filterable接口主要是用来实现自定义匹配规则的,它定义了getFilter()方法
public Filter getFilter() { if (mFilter == null) { mFilter = new CityArrayFilter(); } return mFilter; }
/** *An array filter constrains the content of the array adapter with * a prefix. Each item that does not start with the supplied prefix * is removed from the list.
*/ private class CityArrayFilter extends Filter { /** * The method is called when receive softkeybord entry's char */ @Override protected FilterResults performFiltering(CharSequence prefix) { if(Log.DEBUG.get()) { Log.i(tag, "CityArrayFilter : performFiltering >>>>>>>>>>> prefix = "+prefix); } FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { mOriginalValues = new ArrayList(mObjects); } } if (prefix == null || prefix.length() == 0) { synchronized (mLock) { ArrayList list = new ArrayList (mOriginalValues); results.values = list; results.count = list.size(); } } else { String prefixString = prefix.toString().toLowerCase(); final ArrayList values = mOriginalValues; final int count = values.size(); final ArrayList newValues = new ArrayList (count); final Enumeration enumeration = ChooseCityActivity.ht.keys(); String key; String[] keyPart; while(enumeration.hasMoreElements()) { key = (String)enumeration.nextElement(); keyPart = key.split("-"); Log.i(tag, ">>>>>>>>>>>>>>>>>>> prefixString = "+prefixString+" key = "+key); if(prefixString.length() == 1 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } else if (prefixString.length() == 2) { if(keyPart[2].equals(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } if(newValues.size() == 0 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } } else if (prefixString.length() == 3) { if(keyPart[0].equals(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } if(newValues.size() == 0 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } } } /*for (int i = 0; i < count; i++) { final T 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; 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; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if(Log.DEBUG.get()) { Log.i(tag, "CityArrayFilter : publishResults >>>>>>>>>>> results.values = "+results.values); } mObjects = (List ) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } }
while()里是匹配规则,FilterResults 装的是匹配后的城市,最终它将作为 getView() 的数据源显示在AutoCompleteTextView 的下拉列表里,prefix 指的是监听到的用户输入的拼音字符,通过这个字符去Hashtable里查找对应的城市,具体描述请看看下面的Adapter的完整代码
public class SearchCityAdapterextends BaseAdapter implements Filterable { private static final String tag = Log.getTag(SearchCityAdapter.class); /** * Contains the list of objects that represent the data of this ArrayAdapter. * The content of this list is referred to as "the array" in the documentation. */ private List mObjects; /** * Lock used to modify the content of { @link #mObjects}. Any write operation * performed on the array should be synchronized on this lock. This lock is also * used by the filter (see { @link #getFilter()} to make a synchronized copy of * the original array of data. */ private final Object mLock = new Object(); /** * The resource indicating what views to inflate to display the content of this * array adapter. */ private int mResource; /** * The resource indicating what views to inflate to display the content of this * array adapter in a drop down widget. */ private int mDropDownResource; /** * If the inflated resource is not a TextView, { @link #mFieldId} is used to find * a TextView inside the inflated views hierarchy. This field must contain the * identifier that matches the one defined in the resource file. */ private int mFieldId = 0; /** * Indicates whether or not { @link #notifyDataSetChanged()} must be called whenever * { @link #mObjects} is modified. */ private boolean mNotifyOnChange = true; private Context mContext; private ArrayList mOriginalValues; private CityArrayFilter mFilter; private LayoutInflater mInflater; /** * Constructor * * @param context The current context. * @param textViewResourceId The resource ID for a layout file containing a TextView to use when * instantiating views. */ public SearchCityAdapter(Context context, int textViewResourceId) { init(context, textViewResourceId, 0, new ArrayList ()); } /** * Constructor * * @param context The current context. * @param resource The resource ID for a layout file containing a layout to use when * instantiating views. * @param textViewResourceId The id of the TextView within the layout resource to be populated */ public SearchCityAdapter(Context context, int resource, int textViewResourceId) { init(context, resource, textViewResourceId, new ArrayList ()); } /** * Constructor * * @param context The current context. * @param textViewResourceId The resource ID for a layout file containing a TextView to use when * instantiating views. * @param objects The objects to represent in the ListView. */ public SearchCityAdapter(Context context, int textViewResourceId, T[] objects) { init(context, textViewResourceId, 0, Arrays.asList(objects)); } /** * Constructor * * @param context The current context. * @param resource The resource ID for a layout file containing a layout to use when * instantiating views. * @param textViewResourceId The id of the TextView within the layout resource to be populated * @param objects The objects to represent in the ListView. */ public SearchCityAdapter(Context context, int resource, int textViewResourceId, T[] objects) { init(context, resource, textViewResourceId, Arrays.asList(objects)); } /** * Constructor * * @param context The current context. * @param textViewResourceId The resource ID for a layout file containing a TextView to use when * instantiating views. * @param objects The objects to represent in the ListView. */ public SearchCityAdapter(Context context, int textViewResourceId, List objects) { init(context, textViewResourceId, 0, objects); } /** * Constructor * * @param context The current context. * @param resource The resource ID for a layout file containing a layout to use when * instantiating views. * @param textViewResourceId The id of the TextView within the layout resource to be populated * @param objects The objects to represent in the ListView. */ public SearchCityAdapter(Context context, int resource, int textViewResourceId, List objects) { init(context, resource, textViewResourceId, objects); } /** * Adds the specified object at the end of the array. * * @param object The object to add at the end of the array. */ public void add(T object) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.add(object); if (mNotifyOnChange) notifyDataSetChanged(); } } else { mObjects.add(object); if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Adds the specified Collection at the end of the array. * * @param collection The Collection to add at the end of the array. */ public void addAll(Collection collection) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.addAll(collection); if (mNotifyOnChange) notifyDataSetChanged(); } } else { mObjects.addAll(collection); if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Adds the specified items at the end of the array. * * @param items The items to add at the end of the array. */ public void addAll(T ... items) { if (mOriginalValues != null) { synchronized (mLock) { for (T item : items) { mOriginalValues.add(item); } if (mNotifyOnChange) notifyDataSetChanged(); } } else { for (T item : items) { mObjects.add(item); } if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Inserts the specified object at the specified index in the array. * * @param object The object to insert into the array. * @param index The index at which the object must be inserted. */ public void insert(T object, int index) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.add(index, object); if (mNotifyOnChange) notifyDataSetChanged(); } } else { mObjects.add(index, object); if (mNotifyOnChange) notifyDataSetChanged(); } } /** * Removes the specified object from the array. * * @param object The object to remove. */ public void remove(T object) { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.remove(object); } } else { mObjects.remove(object); } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Remove all elements from the list. */ public void clear() { if (mOriginalValues != null) { synchronized (mLock) { mOriginalValues.clear(); } } else { mObjects.clear(); } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Sorts the content of this adapter using the specified comparator. * * @param comparator The comparator used to sort the objects contained * in this adapter. */ public void sort(Comparator comparator) { Collections.sort(mObjects, comparator); if (mNotifyOnChange) notifyDataSetChanged(); } /** * { @inheritDoc} */ @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); mNotifyOnChange = true; } /** * Control whether methods that change the list ({ @link #add}, * { @link #insert}, { @link #remove}, { @link #clear}) automatically call * { @link #notifyDataSetChanged}. If set to false, caller must * manually call notifyDataSetChanged() to have the changes * reflected in the attached view. * * The default is true, and calling notifyDataSetChanged() * resets the flag to true. * * @param notifyOnChange if true, modifications to the list will * automatically call { @link * #notifyDataSetChanged} */ public void setNotifyOnChange(boolean notifyOnChange) { mNotifyOnChange = notifyOnChange; } private void init(Context context, int resource, int textViewResourceId, List objects) { mContext = context; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mResource = mDropDownResource = resource; mObjects = objects; mFieldId = textViewResourceId; for (T t : objects) { Log.i(tag, ">>>>>>>>>>>>> t = "+t); } } /** * Returns the context associated with this array adapter. The context is used * to create views from the resource passed to the constructor. * * @return The Context associated with this adapter. */ public Context getContext() { return mContext; } /** * { @inheritDoc} */ public int getCount() { return mObjects.size(); } /** * { @inheritDoc} */ public T getItem(int position) { return mObjects.get(position); } /** * Returns the position of the specified item in the array. * * @param item The item to retrieve the position of. * * @return The position of the specified item. */ public int getPosition(T item) { return mObjects.indexOf(item); } /** * { @inheritDoc} */ public long getItemId(int position) { return position; } /** * { @inheritDoc} */ 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("SearchCityAdapter", "You must supply a resource ID for a TextView"); throw new IllegalStateException( "SearchCityAdapter requires the resource ID to be a TextView", e); } T item = getItem(position); Log.i(tag, ">>>>>>>>>>>>>> position = "+position+" item = "+item); if (item instanceof CharSequence) { text.setText((CharSequence)item); } else { text.setText(item.toString()); } return view; } /** * Sets the layout resource to create the drop down views.
* * @param resource the layout resource defining the drop down views * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) */ public void setDropDownViewResource(int resource) { this.mDropDownResource = resource; } /** * { @inheritDoc} */ @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mDropDownResource); } /** * Creates a new ArrayAdapter from external resources. The content of the array is * obtained through { @link android.content.res.Resources#getTextArray(int)}. * * @param context The application's environment. * @param textArrayResId The identifier of the array to use as the data source. * @param textViewResId The identifier of the layout used to create views. * * @return An ArrayAdapter. */ public static ArrayAdapter createFromResource(Context context, int textArrayResId, int textViewResId) { CharSequence[] strings = context.getResources().getTextArray(textArrayResId); return new ArrayAdapter (context, textViewResId, strings); } /** * { @inheritDoc} */ public Filter getFilter() { if (mFilter == null) { mFilter = new CityArrayFilter(); } return mFilter; } /** * An array filter constrains the content of the array adapter with * a prefix. Each item that does not start with the supplied prefix * is removed from the list.
*/ private class CityArrayFilter extends Filter { /** * The method is called when receive softkeybord entry's char */ @Override protected FilterResults performFiltering(CharSequence prefix) { if(Log.DEBUG.get()) { Log.i(tag, "CityArrayFilter : performFiltering >>>>>>>>>>> prefix = "+prefix); } FilterResults results = new FilterResults(); if (mOriginalValues == null) { synchronized (mLock) { mOriginalValues = new ArrayList(mObjects); } } if (prefix == null || prefix.length() == 0) { synchronized (mLock) { ArrayList list = new ArrayList (mOriginalValues); results.values = list; results.count = list.size(); } } else { String prefixString = prefix.toString().toLowerCase(); final ArrayList values = mOriginalValues; final int count = values.size(); final ArrayList newValues = new ArrayList (count); final Enumeration enumeration = ChooseCityActivity.ht.keys(); String key; String[] keyPart; while(enumeration.hasMoreElements()) { key = (String)enumeration.nextElement(); keyPart = key.split("-"); Log.i(tag, ">>>>>>>>>>>>>>>>>>> prefixString = "+prefixString+" key = "+key); if(prefixString.length() == 1 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } else if (prefixString.length() == 2) { if(keyPart[2].equals(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } if(newValues.size() == 0 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } } else if (prefixString.length() == 3) { if(keyPart[0].equals(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } if(newValues.size() == 0 && keyPart[1].startsWith(prefixString)) { newValues.add((T)ChooseCityActivity.ht.get(key)); } } } /*for (int i = 0; i < count; i++) { final T 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; 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; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { if(Log.DEBUG.get()) { Log.i(tag, "CityArrayFilter : publishResults >>>>>>>>>>> results.values = "+results.values); } mObjects = (List ) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } }
本文转自java豆子博客园博客,原文链接:http://www.cnblogs.com/error404/archive/2011/10/10/2205857.html,如需转载请自行联系原作者