问题
I have an listview with filter. When I input some words in an edittext that I used as a filter for example "david", it works well, items in the list are filtered and it will show all item that contains "david". But when I delete some words, for example "dav", the list is still filtered, but it filtered from the last filtered by "david".
Let's say I had 40 items, filtered by "david", it becomes 24 items. Then I filtered it again with "dav", it filtered from the "24 items" one, not the "40 items" one.
Here is my custom adapter:
public class WRegistrantListAdapter extends ArrayAdapter<Registrant> {
private Context mContext;
private int mResource;
private List<Registrant> mOriginalList;
private List<Registrant> mFilteredList;
public WRegistrantListAdapter(Context context, int resource, ArrayList<Registrant> oobjects, int workshopItemId) {
super(context, resource, oobjects);
mContext = context;
mResource = resource;
mFilteredList = oobjects;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
//contains code for displaying item.
}
@NonNull
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults result = new FilterResults();
String constraint = charSequence.toString().toLowerCase();
if (mOriginalList == null) {
mOriginalList = mFilteredList;
Toast.makeText(mContext, String.valueOf(mOriginalList.size()), Toast.LENGTH_SHORT).show();
}
if (constraint == null || constraint.isEmpty() || constraint.equals("")) {
result.values = mOriginalList;
result.count = mOriginalList.size();
} else {
List<Registrant> list = new ArrayList<>();
int max = mOriginalList.size();
for (int cont = 0; cont < max; cont++) {
Registrant item = mOriginalList.get(cont);
boolean contains =
item.getRegistrantName().toLowerCase().contains(constraint) ||
item.getRegistrantNumber().toLowerCase().contains(constraint);
if (contains) {
list.add(mOriginalList.get(cont));
}
}
result.values = list;
result.count = list.size();
}
return result;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
clear();
addAll((ArrayList<Registrant>) results.values);
notifyDataSetChanged();
}
};
}
}
Which part in the filtering is wrong? Any help would be much appreciated. Hope my explanation is not confusing because English is not my mother language.
回答1:
You need two different lists for filtering, so try change mOriginalList = mFilteredList;
to mOriginalList = new ArrayList<>(mFilteredList);
may solve the issue.
Explanations:
mOriginalList = mFilteredList;
is same list with two different names. It is helpful in modular program, just like mFilteredList = oobjects;
in your adapter constructor.
mOriginalList = new ArrayList<>(mFilteredList);
is to make a shallow copy of mFilteredList and store it as mOriginalList, so the lists are different.
Shallow and Deep Copy:
Example: If your custom class, Registrant, contains a public field (List, Map or custom object etc., that requires new for creation) named sample
. Under shallow copy, mOriginalList = new ArrayList<>(mFilteredList);
, mOriginalList.get(i) is a copy of mFilteredList.get(i) and they are 2 different Registrant objects. But mOriginalList.get(i).sample and mFilteredList.get(i).sample is the same object.
If you need mOriginalList.get(i).sample and mFilteredList.get(i).sample to be different objects, then it is called deep copy. There is no ready method to make deep copy, you have to make your own method according to your custom class. But up to now, I never have a case that needs deep copy.
Hope that helps!
回答2:
You should keep two separate lists in your adapter such as,
private List<Registrant> mOriginalList = new ArrayList();
private List<Registrant> mFilteredList = new ArrayList();
public WRegistrantListAdapter(Context context, int resource, ArrayList<Registrant> oobjects, int workshopItemId) {
super(context, resource, oobjects);
mContext = context;
mResource = resource;
mFilteredList.addAll(oobjects);
mOriginalList.addAll(oobjects);
}
Initially, both of them should have the same value and you will use filteredList for showing your data. Later in the filter, you should publish your data like
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredList.clear();
filteredList.addAll((ArrayList<Registrant>) results.values);
notifyDataSetChanged();
}
A complete example is can be found in Filter ListView with arrayadapter
来源:https://stackoverflow.com/questions/60578732/filter-in-listview-did-not-filter-from-the-full-list-but-from-already-filtered