Adam Varhegyi
Adam Varhegyi

Reputation: 9894

How to apply multiple Filters on an Adapter?

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

Answers (3)

kelalaka
kelalaka

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

Apoorv Bambarde
Apoorv Bambarde

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

RaphMclee
RaphMclee

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

Related Questions