• 重写AutoCompleteTextView之实现自定义匹配规则 及 提示


    AutoCompleteTextView 在做搜索功能时常常被应用到,它的好处是根据用户输入的信息实现后缀信息的提示功能。
    此控件通常的用法是

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(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;
    }


    此方法是为了得到一个过滤器,也就是自定义匹配规则的过滤器即CityArrayFilter,它的实现是

    /** 
    * <p>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.</p>
    */
    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<T>(mObjects);
    }
    }
    if (prefix == null || prefix.length() == 0) {
    synchronized (mLock) {
    ArrayList<T> list = new ArrayList<T>(mOriginalValues);
    results.values = list;
    results.count = list.size();
    }
    } else {
    String prefixString = prefix.toString().toLowerCase();
    final ArrayList<T> values = mOriginalValues;
    final int count = values.size();
    final ArrayList<T> newValues = new ArrayList<T>(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<T>) results.values;
    if (results.count > 0) {
    notifyDataSetChanged();
    } else {
    notifyDataSetInvalidated();
    }
    }
    }

    while()里是匹配规则,FilterResults 装的是匹配后的城市,最终它将作为 getView() 的数据源显示在AutoCompleteTextView 的下拉列表里,prefix 指的是监听到的用户输入的拼音字符,通过这个字符去Hashtable里查找对应的城市,具体描述请看看下面的Adapter的完整代码

    public class SearchCityAdapter<T> extends 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<T> 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<T> 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<T>());
    }
    /**
    * 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<T>());
    }
    /**
    * 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<T> 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<T> 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<? extends T> 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<? super T> 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<T> 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;
    }
    /**
    * <p>Sets the layout resource to create the drop down views.</p>
    *
    *
    @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<CharSequence>.
    */
    public static ArrayAdapter<CharSequence> createFromResource(Context context,
    int textArrayResId, int textViewResId) {
    CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
    return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
    }
    /**
    * {
    @inheritDoc}
    */
    public Filter getFilter() {
    if (mFilter == null) {
    mFilter = new CityArrayFilter();
    }
    return mFilter;
    }
    /**
    * <p>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.</p>
    */
    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<T>(mObjects);
    }
    }
    if (prefix == null || prefix.length() == 0) {
    synchronized (mLock) {
    ArrayList<T> list = new ArrayList<T>(mOriginalValues);
    results.values = list;
    results.count = list.size();
    }
    } else {
    String prefixString = prefix.toString().toLowerCase();
    final ArrayList<T> values = mOriginalValues;
    final int count = values.size();
    final ArrayList<T> newValues = new ArrayList<T>(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<T>) results.values;
    if (results.count > 0) {
    notifyDataSetChanged();
    } else {
    notifyDataSetInvalidated();
    }
    }
    }
    }



  • 相关阅读:
    Java实现 蓝桥杯 算法提高 小X的购物计划
    Java实现 蓝桥杯 算法提高 小X的购物计划
    Java实现 第十一届 蓝桥杯 (高职专科组)省内模拟赛
    Java实现 第十一届 蓝桥杯 (高职专科组)省内模拟赛
    Java实现 第十一届 蓝桥杯 (高职专科组)省内模拟赛
    Java 第十一届 蓝桥杯 省模拟赛 小明的城堡
    Java 第十一届 蓝桥杯 省模拟赛 小明的城堡
    Java 第十一届 蓝桥杯 省模拟赛 小明的城堡
    129. Sum Root to Leaf Numbers
    117. Populating Next Right Pointers in Each Node II
  • 原文地址:https://www.cnblogs.com/error404/p/2205857.html
Copyright © 2020-2023  润新知