Reputation: 9894
I got a listView with and a search field that calls my Adapter's getFilter().filter(keyword) func. It is working very nice, but i would like to add an other filter that searches in different tags of my listViews's objects.
So i need two filters for my Adapter, whats the best solution for this?
Thanks,
Upvotes: 2
Views: 6254
Reputation: 5636
I had a similar need and I've written for myself. The filter are combined with AND operator. Keep it as simple as possible. Not claiming it is perfect but it works for me. You can change according to your need.
The ItemModel
public class ItemModel {
public int ID;
public int rating;
public float avg;
public String name;
public String shortDesc;
public boolean read;
}
And the parser.java
/**
* This class is designed to be simple for parsing a filter of the form
* "object field name: variable type: operator: value"
*
* "ID:int:>:20";"name:string:=:20"
* Where ';' means AND, however this is not parsed here.
* If you need different logic, use the results.O
*
* Multiple filters seperated by ';'
*/
public class Parser {
private static final String TAG = "Parser";
public static boolean isPassingTheFiler(String filter, String field, String val) {
String[] mGroups = parseMainGroups(filter);
for (String mGroup : mGroups) {
Log.e(TAG,"Working on the main Group " +mGroup );
String[] subCommand = parseSubCommand(mGroup);
if ( field.equals(subCommand[0])) {
if (!processCommand(subCommand, val)) {
return false;
}
}
}
return true;
}
/**
* parses that data assuming they are all sperated by `;`
*/
public static String[] parseMainGroups(CharSequence commands) {
String buffer = commands.toString();
String parts[] = buffer.split(";");
return parts;
}
public static String[] parseSubCommand(String subCommand) {
//remove the double quotes.
String buffer = removeDoubleQuotes(subCommand.toString());
String parts[] = buffer.split(":");
return parts;
}
public static String removeDoubleQuotes(String quotedString) {
if ((quotedString.charAt(0) == '\"') && (quotedString.charAt(quotedString.length() - 1) == '\"')) {
return quotedString.substring(1, quotedString.length() - 1);
} else {
Log.e(TAG, quotedString + " doesn't contained in double quotes!\nReturned empty string!!!");
return "";
}
}
public static boolean processCommand(String[] subCommand, String val) {
switch (subCommand[1]) {
case "int":
Log.e("TAG","\tint Filer");
return intStatement(subCommand, val);
case "float":
Log.e("TAG","\tfloat Filer");
return floatStatement(subCommand, val);
case "string":
Log.e("TAG","\tString Filer");
return stringStatement(subCommand, val);
default:
return false;
}
}
/**
* Evaluate the Int statement's correctness with the given INT value
*/
public static boolean intStatement(String[] subCommand, String cVal) {
String operString = subCommand[2];
int iVal;
int val;
try {
iVal = Integer.parseInt(subCommand[3]);
val = Integer.parseInt(cVal);
} catch (NumberFormatException e) {
return false;
}
switch (operString) {
case "=":
return val == iVal;
case "<":
return val < iVal;
case ">":
return val > iVal;
case "<=":
return val <= iVal;
case ">=":
return val >= iVal;
case "!=":
return val != iVal;
case "s" :
//digit search as string. We look into string from that we already have
return cVal.contains(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
public static boolean floatStatement(String[] subCommand, String cVal) {
String operString = subCommand[2];
float iVal;
float val;
try {
iVal = Float.parseFloat(subCommand[3]);
val = Float.parseFloat(cVal);
} catch (NumberFormatException e) {
return false;
}
switch (operString) {
case "=":
return val == iVal;
case "<":
return val < iVal;
case ">":
return val > iVal;
case "<=":
return val <= iVal;
case ">=":
return val >= iVal;
case "!=":
return val != iVal;
case "s" :
//digit search as string. We look into string from that we already have
return cVal.contains(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
public static boolean stringStatement(String[] subCommand, String val) {
String operString = subCommand[2];
switch (operString) {
case "=":
//equality
return val.equals(subCommand[3]);
case "<":
//substring
return val.contains(subCommand[3]);
case "sw":
//prefix
return val.startsWith(subCommand[3]);
case "ew":
//postfix
return val.endsWith(subCommand[3]);
default:
Log.e("Parser", "invalid Integer Operation");
return false;
}
}
}
The private filter class in the adapter.
private class ItemFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
String charString = constraint.toString();
String[] parts;
FilterResults filterResults = new FilterResults();
if (charString.isEmpty()) {
filterResults.values = new ArrayList<>(itemList);
} else {
//Parse the main group
parts = parseMainGroups(charString);
if (parts.length < 1) {
filterResults.values = new ArrayList<>(itemList);
} else {
List<ItemModel> filteredList = new ArrayList<>();
for (ItemModel row : itemList) {
if ( !isPassingTheFiler(charString,"ID",""+row.ID)) {
continue;
} else {
Log.e("Filter", "passed on ID" + row.ID);
}
if ( !isPassingTheFiler(charString,"name",""+row.name)) {
continue;
} else {
Log.e("Filter", "passed on name" + row.name);
}
// passed every test asked. If empty they returned true!
filteredList.add(row);
}
filterResults.values = new ArrayList<>(filteredList);
}
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
updateList((List<ItemModel>) results.values);
}
}
And the updateList member funtion for the adapter
public void updateList(List<ItemModel> newList) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ItemDiffUtilCallback(newList, itemListFiltered));
itemListFiltered.clear();
itemListFiltered.addAll(newList);
diffResult.dispatchUpdatesTo(this);
Log.e("TAG", "updated with dispatch");
}
And the difutils that helps the good animation
public class ItemDiffUtilCallback extends DiffUtil.Callback {
private List<ItemModel> oldList;
private List<ItemModel> newList;
public ItemDiffUtilCallback(List<ItemModel> newList, List<ItemModel> oldList) {
this.newList = newList;
this.oldList = oldList;
}
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).ID == newList.get(newItemPosition).ID;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
Upvotes: 0
Reputation: 342
Apply Multiple Filter in Listview and also use the multi sorting in ListView, try this link:
https://github.com/apurv3039/filter_listview/tree/master
Upvotes: 0
Reputation: 1623
I suppose that you implemented the filter yourself. As you cant get two filters you could have a field in the filter that defines what kind of filtering should by applied ( you could use multiple filters in your filter).
Set you field of the filter to the value you want before using the filter.
Or:
Use the keyword to choose the filter to apply. Add on the beginning of the keyword some characters which define the filter to apply. With String.beginsWith()
you can check which type of filtering has to by applied. This has to be done in the Filter itself. The caller of the getFilter.filter(keyword) has to know what characters have to by added in front of the string.
Upvotes: 5