Android AutoCompleteTextView与自定义适配器筛选不起作用

我有Custom CustomerAdapter

public class CustomerAdapter extends ArrayAdapter<Customer> { private final String MY_DEBUG_TAG = "CustomerAdapter"; private ArrayList<Customer> items; private int viewResourceId; public CustomerAdapter(Context context, int viewResourceId, ArrayList<Customer> items) { super(context, viewResourceId, items); this.items = items; this.viewResourceId = viewResourceId; } public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(viewResourceId, null); } Customer customer = items.get(position); if (customer != null) { TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel); if (customerNameLabel != null) { customerNameLabel.setText(String.valueOf(customer.getName())); } } return v; } } 

customer_auto布局

 <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/customerNameLabel" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" android:textSize="16sp" android:textColor="#000"> </TextView> 

并在我的public void onCreate

 AutoCompleteTextView customerAutoComplete = (AutoCompleteTextView) findViewById(R.id.autocomplete_customer); CustomerAdapter customerAdapter = new CustomerAdapter(this, R.layout.customer_auto, customerList); customerAutoComplete.setAdapter(customerAdapter); 

和Customer.java

 public class Customer implements Parcelable { private int id; private String name = ""; public Customer() { // TODO Auto-generated constructor stub } /** * This will be used only by the MyCreator * * @param source */ public Customer(Parcel source) { /* * Reconstruct from the Parcel */ id = source.readInt(); name = source.readString(); } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public int getId() { return this.id; } public String getName() { return this.name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public Customer createFromParcel(Parcel source) { return new Customer(source); } @Override public Customer[] newArray(int size) { return new Customer[size]; // TODO Auto-generated method stub } }; @Override public String toString() { return this.name; } } 

但是自动提示框不能正确过滤。 例如; 如果我在testing盒中键入anbr开头的客户,

我必须重载适配器的getFilter()方法

这是我工作的代码,感谢sacoskun

 public class CustomerAdapter extends ArrayAdapter<Customer> { private final String MY_DEBUG_TAG = "CustomerAdapter"; private ArrayList<Customer> items; private ArrayList<Customer> itemsAll; private ArrayList<Customer> suggestions; private int viewResourceId; public CustomerAdapter(Context context, int viewResourceId, ArrayList<Customer> items) { super(context, viewResourceId, items); this.items = items; this.itemsAll = (ArrayList<Customer>) items.clone(); this.suggestions = new ArrayList<Customer>(); this.viewResourceId = viewResourceId; } public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(viewResourceId, null); } Customer customer = items.get(position); if (customer != null) { TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel); if (customerNameLabel != null) { // Log.i(MY_DEBUG_TAG, "getView Customer Name:"+customer.getName()); customerNameLabel.setText(customer.getName()); } } return v; } @Override public Filter getFilter() { return nameFilter; } Filter nameFilter = new Filter() { @Override public String convertResultToString(Object resultValue) { String str = ((Customer)(resultValue)).getName(); return str; } @Override protected FilterResults performFiltering(CharSequence constraint) { if(constraint != null) { suggestions.clear(); for (Customer customer : itemsAll) { if(customer.getName().toLowerCase().startsWith(constraint.toString().toLowerCase())){ suggestions.add(customer); } } FilterResults filterResults = new FilterResults(); filterResults.values = suggestions; filterResults.count = suggestions.size(); return filterResults; } else { return new FilterResults(); } } @Override protected void publishResults(CharSequence constraint, FilterResults results) { ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values; if(results != null && results.count > 0) { clear(); for (Customer c : filteredList) { add(c); } notifyDataSetChanged(); } } }; } 

这是我的解决scheme。 我觉得它比一个更清洁(不使用3个单独的,混乱的ArrayLists)比接受的,有更多的select。

这是我经过一些试验和错误之后所做的。 即使用户键入退格键,它也应该起作用,因为它不会从mCustomers删除原始条目:

 public class CustomerAdapter extends ArrayAdapter<Customer> { private LayoutInflater layoutInflater; List<Customer> mCustomers; private Filter mFilter = new Filter() { @Override public String convertResultToString(Object resultValue) { return ((Customer)resultValue).getName(); } @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint != null) { ArrayList<Customer> suggestions = new ArrayList<Customer>(); for (Customer customer : mCustomers) { // Note: change the "contains" to "startsWith" if you only want starting matches if (customer.getName().toLowerCase().contains(constraint.toString().toLowerCase())) { suggestions.add(customer); } } results.values = suggestions; results.count = suggestions.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { clear(); if (results != null && results.count > 0) { // we have filtered results addAll((ArrayList<Customer>) results.values); } else { // no filter, add entire original list back in addAll(mCustomers); } notifyDataSetChanged(); } }; public CustomerAdapter(Context context, int textViewResourceId, List<Customer> customers) { super(context, textViewResourceId, customers); // copy all the customers into a master list mCustomers = new ArrayList<Customer>(customers.size()); mCustomers.addAll(customers); layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { view = layoutInflater.inflate(R.layout.customerNameLabel, null); } Customer customer = getItem(position); TextView name = (TextView) view.findViewById(R.id.customerNameLabel); name.setText(customer.getName()); return view; } @Override public Filter getFilter() { return mFilter; } } 

在上面的代码中, publisHResults()方法给出了并发修改exception….我们必须修改代码为:

 @Override protected void publishResults(CharSequence constraint, FilterResults results) { ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values; ArrayList<Customer> customerList=new ArrayList<Customer>(); if (results != null && results.count > 0) { clear(); for (Customer c : filteredList) { customerList.add(c); } Iterator<Customer> customerIterator=getResult.iterator(); while (customerIterator.hasNext()) { Customer customerIterator=customerIterator.next(); add(customerIterator); } notifyDataSetChanged(); } } 

也许这是为时已晚,你不需要覆盖所有这些function,唯一覆盖的function是:

  public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(viewResourceId, null); } Customer customer = getItem(position); if (customer != null) { TextView customerNameLabel = (TextView) v.findViewById(R.id.customerNameLabel); if (customerNameLabel != null) { customerNameLabel.setText(String.valueOf(customer.getName())); } } return v; } 

考虑我改变:

  Customer customer = items.get(position); Customer customer = getItem(position); 

注意,你不应该声明新的ListItems,

  private ArrayList<Customer> items; 

因为ArrayAdapter使用自己的mObjects,并且过滤这个列表而不是你的项目列表,所以你应该使用getItem函数来访问项目。 那么没有理由写你的ArrayFilter。

而不是在适配器中重写getFilter()方法,只要我们可以覆盖userDefined对象(Customer)的toString()。在toString()只是返回基于你需要过滤的字段。它为我工作。在我的例子即时过滤基于名称。

 public class Customer{ private int id; private String name; @Override public String toString() { return this.name; } } 

我不知道你在哪里检索getResult。 我认为这种情况下的解决scheme是没有并发修改的:

 @Override protected void publishResults(CharSequence constraint, FilterResults results) { ArrayList<Customer> filteredList = (ArrayList<Customer>) results.values; ArrayList<Customer> customerList=new ArrayList<Customer>(); if (results != null && results.count > 0) { clear(); try{ for (Customer c : filteredList) { customerList.add(c); } }catch(Exception e){ Log.e("PEEEETAAAAAAAA", "AutoCompletaError: "+e.getMessage()+" "+e.getCause()+" "+e.getLocalizedMessage()); } Iterator<Customer> customerIterator=customerList.iterator(); while (customerIterator.hasNext()) { Customer customerIterator=customerIterator.next(); add(customerIterator); } notifyDataSetChanged(); } } 

我有非更新,并从上面的答案修改原始列表问题。 我用这个代码解决了这个问题。

 public class AdapterAutoCompleteTextView extends ArrayAdapter<ItemWord> { private int LayoutID; private int TextViewID; private LayoutInflater Inflater; private List<ItemWord> ObjectsList; public AdapterAutoCompleteTextView(Context ActivityContext, int ResourceID, int TextViewResourceID, List<ItemWord> WordList) { super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList<ItemWord>()); LayoutID = ResourceID; TextViewID = TextViewResourceID; ObjectsList = WordList; Inflater = LayoutInflater.from(ActivityContext); } @Override public View getView(int Position, View ConvertView, ViewGroup Parent) { ItemWord Word = getItem(Position); if(ConvertView == null) { ConvertView = Inflater.inflate(LayoutID, null); ResultHolder Holder = new ResultHolder(); Holder.ResultLabel= (TextView) ConvertView.findViewById(TextViewID); ConvertView.setTag(Holder); } ResultHolder Holder = (ResultHolder) ConvertView.getTag(); Holder.ResultLabel.setText(Word.getSpelling()); return ConvertView; } @Override public Filter getFilter() { return CustomFilter; } private Filter CustomFilter = new Filter() { @Override public CharSequence convertResultToString(Object ResultValue) { return ((ItemWord) ResultValue).getSpelling(); } @Override protected FilterResults performFiltering(CharSequence Constraint) { FilterResults ResultsFilter = new FilterResults(); ArrayList<ItemWord> OriginalValues = new ArrayList<ItemWord>(ObjectsList); if(Constraint == null || Constraint.length() == 0){ ResultsFilter.values = OriginalValues; ResultsFilter.count = OriginalValues.size(); } else { String PrefixString = Constraint.toString().toLowerCase(); final ArrayList<ItemWord> NewValues = new ArrayList<ItemWord>(); for(ItemWord Word : OriginalValues){ String ValueText = Word.getSpelling().toLowerCase(); if(ValueText.startsWith(PrefixString)) NewValues.add(Word); } ResultsFilter.values = NewValues; ResultsFilter.count = NewValues.size(); } return ResultsFilter; } @Override protected void publishResults(CharSequence Constraint, FilterResults Results) { clear(); if(Results.count > 0) addAll(((ArrayList<ItemWord>) Results.values)); else notifyDataSetInvalidated(); } }; private static class ResultHolder { TextView ResultLabel; } } 

这是非更新和修改原始列表问题最重要的一行:

 super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList<ItemWord>()); 

特别是那些

super(ActivityContext,ResourceID,TextViewResourceID, new ArrayList() );

我希望这个解决scheme会帮助你:)

如果得到ConcurrentModificationExceptionexception。

将ArrayListreplace为线程安全的CopyOnWriteArrayList。

在这里你可以finddetatils。

https://examples.javacodegeeks.com/java-basics/exceptions/java-util-concurrentmodificationexception-how-to-handle-concurrent-modification-exception/

我希望这篇文章能够帮助人们在未来实现类似的自定义function。 我基于我的这个版本的适配器,用于在我的微博客应用中显示标签build议:

 public class TagSuggestionsAdapter extends ArrayAdapter<String> implements Filterable 

扩展ArrayAdapter以减less样板代码。 实施可筛选以后更改筛选器行为。

  private List<String> allTags; private List<String> tagSuggestions; private Context context; public TagSuggestionsAdapter(List<String> initialTagSuggestions, List<String> allTags, Context context) { super(context, R.layout.item_tag_suggestion, initialTagSuggestions); this.tagSuggestions = initialTagSuggestions; this.allTags = allTags; this.context = context; } 

基本上在构造函数中,您需要传递一个最初将显示的列表 – 稍后将成为一个带有过滤结果的列表(这也是对调用notifyDataSetChanged()时将要考虑的列表的引用),显然是列表你可以根据你的过滤(allTags在我的情况)。 我也在getView()中传递布局膨胀的上下文。

  @NonNull @Override public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(context) .inflate(R.layout.item_tag_suggestion, parent, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.tagSuggestionTextView.setText(tagSuggestions.get(position)); return convertView; } static class ViewHolder { @BindView(R.id.tag_suggestion_text_view) TextView tagSuggestionTextView; ViewHolder(View itemView) { ButterKnife.bind(this, itemView); } } 

在上面,你可以看到一个简单的视图持有人模式,从Butterknife的一点点帮助膨胀自定义行布局。

  @NonNull @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { if (constraint != null) { List<String> filteredTags = filterTagSuggestions(constraint.toString(), allTags); FilterResults filterResults = new FilterResults(); filterResults.values = filteredTags; filterResults.count = filteredTags.size(); return filterResults; } else { return new FilterResults(); } } @Override protected void publishResults(CharSequence constraint, FilterResults results) { tagSuggestions.clear(); if (results != null && results.count > 0) { List<?> filteredTags = (List<?>) results.values; for (Object filteredTag : filteredTags) { if (filteredTag instanceof String) { tagSuggestions.add((String) filteredTag); } } } notifyDataSetChanged(); } }; } 

这是我写的最less的样板代码。 你唯一需要关注的是方法filterTagSuggestions ,它应该根据用户的input( CharSequence constraint )返回一个过滤的标签列表。 希望总结和组织一些必要的信息。