filtering custom adapter IndexOutOfBoundsException - android

I'm novice at android.
My custom Adapter cause exception when filtering.
here is my code.
private class DeptAdapter extends ArrayAdapter implements Filterable {
private ArrayList<Dept> items;
private ArrayList<Dept> mOriginalValues;
public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.item_listview_2line, null);
}
Dept d = items.get(position);
if (d != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
if (tt != null){
tt.setText(d.dept_nm);
}
if(bt != null){
bt.setText(d.dept_cd);
}
}
return v;
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
items = (ArrayList<Dept>) results.values; // has the filtered values
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<Dept> FilteredArrList = new ArrayList<Dept>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Dept>(items); // saves the original data in mOriginalValues
}
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
Dept d = mOriginalValues.get(i);
if (d.dept_cd.toLowerCase().startsWith(constraint.toString()) || d.dept_nm.toLowerCase().startsWith(constraint.toString())) {
FilteredArrList.add(d);
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
class Dept
{
String dept_cd;
String dept_nm;
public Dept(String dept_cd, String dept_nm)
{
this.dept_cd = dept_cd;
this.dept_nm = dept_nm;
}
public String toString()
{
return this.dept_nm+ "(" + this.dept_cd +")" ;
}
}
help me anyone....
I can't understand why getView() method was invoked more then items.size()

Keep in mind that getView() will query the size of the items that the superclass has, which right now, is what you originally passed it when calling the superclass constructor,
super(context, textViewResourceId, items);
Therefore, the superclass doesn't know that you've changed the size when you have filtered. This means getCount() will return the original size of the array, which is understandably larger than your filtered array.
This means You should override the getCount() method so you're sure that you're returning the actual valid size:
#Override
public int getCount()
{
return items.size();
}
You should also override the other methods related to the List operations (such as getting) if you are going to be using them.
Eg:
#Override
public Dept getItem (int pos){
return items.get(pos);
}

You need to add this methods for better performance:
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return this.items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}

You are missing getCount() method,
look at this demo
I hope it will be helpful !

private ArrayList<Dept> items;
private ArrayList<Dept> mOriginalValues;
public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
super(context, textViewResourceId, items);
this.items = items;
this.mOriginalValues=items; //add this line in your code
}

Related

Incompatible type in Android AutoCompleteTextView

I'm trying to add multiple values in AutoCompleteTextView from SQlite Database in Android.But it shows incompatible type for this method =
#Override
public String getItem(int position) {
return fullList.get(position);
}
public class AutoCompleteAdapter extends ArrayAdapter<AutoCompleteObject> implements Filterable {
private ArrayList<AutoCompleteObject> fullList;
private ArrayList<AutoCompleteObject> mOriginalValues;
private ArrayFilter mFilter;
public AutoCompleteAdapter(Context context, int resource, int textViewResourceId, ArrayList<AutoCompleteObject> fullList) {
super(context, resource, textViewResourceId, fullList);
this.fullList = fullList;
mOriginalValues = new ArrayList<AutoCompleteObject>(fullList);
}
#Override
public int getCount() {
return fullList.size();
}
#Override
public String getItem(int position) {
return fullList.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(position, convertView, parent);
}
#Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class ArrayFilter extends Filter {
private Object lock;
#Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (lock) {
mOriginalValues = new ArrayList<AutoCompleteObject>(fullList);
}
}
if (prefix == null || prefix.length() == 0) {
synchronized (lock) {
ArrayList<AutoCompleteObject> list = new ArrayList<AutoCompleteObject>(
mOriginalValues);
results.values = list;
results.count = list.size();
}
} else {
final String prefixString = prefix.toString().toLowerCase();
ArrayList<AutoCompleteObject> values = mOriginalValues;
int count = values.size();
ArrayList<AutoCompleteObject> newValues = new ArrayList<AutoCompleteObject>(count);
for (int i = 0; i < count; i++) {
String item = values.get(i);
if (item.toLowerCase().contains(prefixString)) {
newValues.add(item);
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,
Filter.FilterResults results) {
if (results.values != null) {
fullList = (ArrayList<AutoCompleteObject>) results.values;
} else {
fullList = new ArrayList<AutoCompleteObject>();
}
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
How about this approach?
#Override
public String getItem(int position) {
AutoCompleteObject obj = fullList.get(position);
String item = obj.getYourValue() + " " + obj.getAnotherValue();
return item;
}
Of course, it means that you need to change generic type to String.
your getItem is returning a String, but fullList contains AutoCompleteObject. You should change it like
#Override
public AutoCompleteObject getItem(int position) {
return fullList.get(position);
}
your Adapter has generic type AutoCompleteObject (ArrayAdapter<AutoCompleteObject>). Also the super.getItem returns T, so you can't really change the signature of the method to make it return a String. If you do so you will get another compile time error

filter in Autocompletetextview arrayadapter do not get the desired result

What is the wrong in my code?
below is the custom adapter for AutoCompleteTextView, I override basic methods, and also override the filter method, but it do not give me the desired result.
public class AirportCodesAdapter extends ArrayAdapter<AirportCodes> {
public Context mContext;
public ArrayList<AirportCodes> arrayList;
public int layoutResourceId;
public AirportCodesAdapter(Context mContext, int layoutResourceId, ArrayList<AirportCodes> arrayList) {
super(mContext, layoutResourceId, arrayList);
this.layoutResourceId = layoutResourceId;
this.mContext = mContext;
this.arrayList = arrayList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_row_airports_codes, parent, false);
}
TextView Code = (TextView) convertView.findViewById(R.id.code_text_list_row_airport_codes);
TextView airport = (TextView) convertView.findViewById(R.id.airport_name_list_row_airport_codes);
Code.setText(arrayList.get(position).getCode());
airport.setText(arrayList.get(position).getAirport());
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
#Override
public AirportCodes getItem(int position) {
return arrayList.get(position);
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public Filter getFilter() {
return nameFilter;
}
Filter nameFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<AirportCodes> tempList = new ArrayList<AirportCodes>();
//constraint is the result from text you want to filter against.
//objects is your data set you will filter from
if (constraint != null && arrayList != null) {
int length = arrayList.size();
int i = 0;
while (i < length) {
for (AirportCodes customer : arrayList) {
if(customer.getCode().toString().trim().toUpperCase().startsWith(constraint.toString().trim().toUpperCase()) {
tempList.add(customer);
}
}
// AirportCodes item = arrayList.get(i);
// //do whatever you wanna do herem
// //adding result set output array
// tempList.add(item);
i++;
}
//following two lines is very important
//as publish result can only take FilterResults objects
filterResults.values = tempList;
filterResults.count = tempList.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
arrayList = (ArrayList<AirportCodes>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
//
//
// if (results != null && results.count > 0) {
// suggestions = (ArrayList<AirportCodes>) results.values;
// notifyDataSetChanged();
// } else {
// notifyDataSetInvalidated();
// }
}
};
}
I have spent more than 6 hours trying to find why the code shows only list of airports and filter do not filter the results.
EDIT:
the desired behavior is to display only the texts that have (startsWith) the typed text in AutoCompleteTextView so I can send it to the previous activity.
and I editied my code to be more appropriate
I hope my question is clear .

Android AutoComplete with custom filter having duplicate results

Good day, I have this custom adapter with a filterable interface implemented and am getting duplicate values in the resulting list.
SearchAutoCompleteAdapter.java
public class SearchAutoCompleteAdapter extends BaseAdapter implements Filterable {
private ArrayList<BaseAutocompleteItems> resultList;
List<BaseAutocompleteItems> filteredProducts;
private LayoutInflater layoutInflater;
private Context context;
private int layout;
SearchAutoCompleteAPI searchautocomplete = new SearchAutoCompleteAPI();
public SearchAutoCompleteAdapter(Context context, int resource) {
super();
this.context = context;
this.layout = resource;
filteredProducts = new ArrayList<BaseAutocompleteItems>();
resultList = new ArrayList<BaseAutocompleteItems>();
}
#Override
public int getCount() {
return resultList.size();
}
#Override
public Object getItem(int index) {
return resultList.get(index);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(layout, null);
}
TextView name = (TextView) convertView.findViewById(R.id.suggestion_text_id);
name.setText(resultList.get(position).getName());
return convertView;
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
List<BaseAutocompleteItems> tempfilteredProducts = new ArrayList<BaseAutocompleteItems
filteredProducts.clear();
if (constraint != null || constraint.length() > 0) {
tempfilteredProducts.clear();
tempfilteredProducts = searchautocomplete.autocomplete(constraint.toString()); //webservice call
} else {
tempfilteredProducts = new ArrayList<BaseAutocompleteItems>();
}
for (BaseAutocompleteItems items : tempfilteredProducts) {
if (items.getName().contains(constraint.toString())) {
filteredProducts.add(items);
}
}
filterResults.values = filteredProducts;
filterResults.count = filteredProducts.size();
return filterResults;
}
#Override
protected void publishResults (CharSequence constraint, FilterResults results){
resultList = (ArrayList<BaseAutocompleteItems>)results.values;
if(results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
;
return filter;
}
}
If I type "yell" and press backspace for "yel" or increase my char to "yello", I get the same result and thus the ArrayList ends up with duplicated items. I have tried clearing the lists before populating the list but nothing seems to work.
Nothing wrong with the code in the question. just a checklist for anyone first. make sure you call Clear() on the ArrayList being returned in the line.
tempfilteredProducts = searchautocomplete.autocomplete(constraint.toString()); //webservice call
from the API call first before populating the values from the webservice and sending it back to tempfilteredProducts(i.e before every api request). That way you avoid duplicate values from the autocompletetextview string as in my case in the question.
Try changing
if (items.getName().contains(constraint.toString()))
to
if (items.getName().startsWith(constraint.toString()))

Filter on custom Arrayadapter doesnt work [duplicate]

I'm novice at android.
My custom Adapter cause exception when filtering.
here is my code.
private class DeptAdapter extends ArrayAdapter implements Filterable {
private ArrayList<Dept> items;
private ArrayList<Dept> mOriginalValues;
public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.item_listview_2line, null);
}
Dept d = items.get(position);
if (d != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
if (tt != null){
tt.setText(d.dept_nm);
}
if(bt != null){
bt.setText(d.dept_cd);
}
}
return v;
}
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
items = (ArrayList<Dept>) results.values; // has the filtered values
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); // Holds the results of a filtering operation in values
ArrayList<Dept> FilteredArrList = new ArrayList<Dept>();
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Dept>(items); // saves the original data in mOriginalValues
}
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = mOriginalValues.size();
results.values = mOriginalValues;
} else {
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < mOriginalValues.size(); i++) {
Dept d = mOriginalValues.get(i);
if (d.dept_cd.toLowerCase().startsWith(constraint.toString()) || d.dept_nm.toLowerCase().startsWith(constraint.toString())) {
FilteredArrList.add(d);
}
}
// set the Filtered result to return
results.count = FilteredArrList.size();
results.values = FilteredArrList;
}
return results;
}
};
return filter;
}
}
class Dept
{
String dept_cd;
String dept_nm;
public Dept(String dept_cd, String dept_nm)
{
this.dept_cd = dept_cd;
this.dept_nm = dept_nm;
}
public String toString()
{
return this.dept_nm+ "(" + this.dept_cd +")" ;
}
}
help me anyone....
I can't understand why getView() method was invoked more then items.size()
Keep in mind that getView() will query the size of the items that the superclass has, which right now, is what you originally passed it when calling the superclass constructor,
super(context, textViewResourceId, items);
Therefore, the superclass doesn't know that you've changed the size when you have filtered. This means getCount() will return the original size of the array, which is understandably larger than your filtered array.
This means You should override the getCount() method so you're sure that you're returning the actual valid size:
#Override
public int getCount()
{
return items.size();
}
You should also override the other methods related to the List operations (such as getting) if you are going to be using them.
Eg:
#Override
public Dept getItem (int pos){
return items.get(pos);
}
You need to add this methods for better performance:
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return this.items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
You are missing getCount() method,
look at this demo
I hope it will be helpful !
private ArrayList<Dept> items;
private ArrayList<Dept> mOriginalValues;
public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
super(context, textViewResourceId, items);
this.items = items;
this.mOriginalValues=items; //add this line in your code
}

Android AutoCompleteTextView with Custom Adapter filtering not working

I've the 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;
}
}
and customer_auto layout
<?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>
and on my public void onCreate
AutoCompleteTextView customerAutoComplete = (AutoCompleteTextView) findViewById(R.id.autocomplete_customer);
CustomerAdapter customerAdapter = new CustomerAdapter(this, R.layout.customer_auto, customerList);
customerAutoComplete.setAdapter(customerAdapter);
and 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;
}
}
But the auto suggest box does not filter correctly. for eg; if i type an in the test box customers starting with br are showing up!
I have to over-ride the getFilter() method of the Adapter
Here is the code which worked for me, thanks to 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();
}
}
};
}
This is my solution. I feel like it's a bit cleaner (doesn't use 3 separate, confusing ArrayLists) than the accepted one, and has more options. It should work even if the user types backspace, because it doesn't remove the original entries from mCustomers (unlike the accepted answer):
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;
}
}
Instead of overriding getFilter() method in adapter, simply we can override the toString() of the userDefined object (Customer).
In toString() just return the field based on what you need to filter. It worked for me.
In my example I'm filtering based on names:
public class Customer{
private int id;
private String name;
#Override
public String toString() {
return this.name;
}
}
In the above code publisHResults() method gives the concurrent modification exception....
we have to modify the code as:
#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();
}
}
Maybe this is too late, you dont need to override all of these functions , the only function to override is :
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;
}
consider I change :
Customer customer = items.get(position);
Customer customer = getItem(position);
pay attention, you should not declare new ListItems,
private ArrayList<Customer> items;
because ArrayAdapter works with its own mObjects, and filter this list not your items list,
So you should use getItem function to access items.
then there is no reason to write your ArrayFilter.
I don't know where you retrieving the getResult. I think the solution in this case for don't have the concurrent modification is:
#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();
}
}
I hope that this post will help people with implementation of a similar custom functionality in the future. I based this on my version of adapter used for displaying tag suggestions in my microblogging app:
public class TagSuggestionsAdapter extends ArrayAdapter<String> implements Filterable
Extending ArrayAdapter to have less boilerplate code. Implementing Filterable to change filter behavior later.
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;
}
Basically in constructor you need to pass a list that will be displayed initially - it'll later become a list with filtered results (this is also a reference to a list that will be taken into consideration when calling notifyDataSetChanged()) and obviously a list on which you can base your filtering (allTags in my case). I'm also passing Context for layout inflation in 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);
}
}
Above you can see a simple view holder pattern with a little help from Butterknife to inflate a custom row layout.
#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();
}
};
}
This is the least boilerplate code I could write. Your only concern is method filterTagSuggestions that should return a filtered list of tags based on input from user (CharSequence constraint). Hope that summarized and organized necessary info a little bit.
If you get ConcurrentModificationException exception.
Replace ArrayList with the thread safe CopyOnWriteArrayList.
Here you can find detatils answer
I have non-update and modify orginal list issues from above answer. I fixed this problem with this codes.
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;
}
}
This is most important line for non-update and modify orginal list issue:
super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList<ItemWord>());
Particularly those
super(ActivityContext, ResourceID, TextViewResourceID, new ArrayList());
I hope this solution will be help you :)

Categories

Resources