How to implement getFilter on a BaseAdapter?

2020-02-05 06:19发布

问题:

I am trying to implement a getFilter() on a base adapter to filter out search results on a List. Is there any example of how to implement a getFilter()?

MainActivity.java

   final AppInfoAdapter adapter = new AppInfoAdapter(this, Utilities.getSystemFilteredApplication(this), getPackageManager());


        public void onTextChanged(CharSequence s, int start, int before,
                int count) {
           adapter.getFilter().filter(s); //Filter from my adapter
           adapter.notifyDataSetChanged(); //Update my view
        }

AppInfoAdapter.java

package com.example.permission;

import java.util.List;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;

public class AppInfoAdapter extends BaseAdapter implements Filterable{
    private Context mContext;
    private List mListAppInfo;
    PackageManager mPackManager;

    public AppInfoAdapter(Context c, List list, PackageManager pm) {
        mContext = c;
        mListAppInfo = list;
        mPackManager = pm;
    }

    public int getCount() {
        return mListAppInfo.size();
    }


    public Object getItem(int position) {
        return mListAppInfo.get(position);
    }


    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        // get the selected entry
        ApplicationInfo entry = (ApplicationInfo) mListAppInfo.get(position);

        // reference to convertView
        View v = convertView;

        // inflate new layout if null
        if(v == null) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            v = inflater.inflate(R.layout.layout_appinfo, null);
        }

        // load controls from layout resources
        ImageView ivAppIcon = (ImageView)v.findViewById(R.id.ivIcon);
        TextView tvAppName = (TextView)v.findViewById(R.id.tvName);
        TextView tvPkgName = (TextView)v.findViewById(R.id.tvPack);

        // set data to display
        ivAppIcon.setImageDrawable(entry.loadIcon(mPackManager));
        tvAppName.setText(entry.loadLabel(mPackManager));
        tvPkgName.setText(entry.packageName);

        // return view
        return v;
    }

    public Filter getFilter() {
        // TODO Auto-generated method stub
        return null;
    }


}

EDIT: Edited the code and added full AppInfoAdapter.java

回答1:

in your adapter put this class to use it in getfilter method

//this is a simple class that filtering the ArrayList of strings used in adapter

public class filter_here extends Filter{

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            // TODO Auto-generated method stub

            FilterResults Result = new FilterResults();
            // if constraint is empty return the original names
            if(constraint.length() == 0 ){
                Result.values = Original_Names;
                Result.count = Original_Names.size();
                return Result;
            }

            ArrayList<String> Filtered_Names = new ArrayList<String>();
            String filterString = constraint.toString().toLowerCase();
            String filterableString;

            for(int i = 0; i<Original_Names.size(); i++){
                filterableString = Original_Names.get(i);
                if(filterableString.toLowerCase().contains(filterString)){
                    Filtered_Names.add(filterableString);
                }
            }
            Result.values = Filtered_Names;
            Result.count = Filtered_Names.size();

            return Result;
        }

        @Override
        protected void publishResults(CharSequence constraint,FilterResults results) {
            // TODO Auto-generated method stub
            Names = (ArrayList<String>) results.values;
            notifyDataSetChanged();
        }

    }

return instance from it in getfilter

@Override
    public Filter getFilter() {
        // TODO Auto-generated method stub
        return filter;
    }

full example



回答2:

this almost got me killed :)

  1. implement your BaseAdapter like this:
  2. define an ArrayList of List in your public adapter class which is gonna contain temporary items of your Original List.

    public class MyAdapter extends BaseAdapter implements Filterable{
    
        public static ArrayList<String> temporarylist;
        public static ArrayList<String> OriginalList;
        private Activity activity;
    
        public MyAdapter(Activity activity, ArrayList<String> OriginalList) {
             super();
             this.activity=activity;
             this.OriginalList = OriginalList;
             temporarylist=OriginalList;
    
        }
        .
        .
        .
    
  3. create getFilter() method with the following code[as an example]:

    public Filter getFilter() {
        Filter filter = new Filter() {
    
        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            temporarylist=(ArrayList<String>)results.values;
            notifyDataSetChanged();
        }
    
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();
            ArrayList<String> FilteredList= new ArrayList<String>();
            if (constraint == null || constraint.length() == 0) {
                // No filter implemented we return all the list
                results.values = OriginalList;
                results.count = OriginalList.size();
            }
            else {
                for (int i = 0; i < OriginalList.size(); i++) {
                    String data = OriginalList.get(i);
                    if (data.toLowerCase().contains(constraint.toString()))  {
                        FilteredList.add(data);
                    }
                }
                results.values = FilteredList;
                results.count = FilteredList.size();
            }
            return results;
        }
    };
    return filter;
    }
    

And finally in your activity for your EditText:

MyAdapter adapter;
ArrayList<String> items;


ListView list = (ListView) findViewById(R.id.list);
items = new ArrayList<String>();
for (int i=0;i<30;i++){
     items.add("Hello world "+String.valueof(i));
}
adapter = new GameAdapter(this, items);
list.setAdapter(adapter);


EditText inputSearch = (EditText) findViewById(R.id.Search_txt);
     inputSearch.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
                // When user changed the Text
                MyActivity.this.adapter.getFilter().filter(cs);
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                    int arg3) {
                // TODO Auto-generated method stub

            }

            @Override
            public void afterTextChanged(Editable arg0) {
                // TODO Auto-generated method stub                          
            }
        });


回答3:

You need to return an instance of a Filter. To write a filter, subclass Filter and implement performFiltering and publishResults. See the docs.



回答4:

Can you post your full AppInfoAdapter? Also is there any reason extending from BaseAdapter and not ArrayAdapter? If you have an ArrayList of objects, use ArrayAdapter, it already implements Filterable interface.

Actually you are using a List, your adapter can be rewritten to extends ArrayAdapter which already is Filterable.

public class AppInfoAdapter extends ArrayAdapter<ApplicationInfo> {

    private Context mContext;
    PackageManager mPackManager;

    public AppInfoAdapter(Context c, List<ApplicationInfo> list, PackageManager pm) {
        super(c, 0, new ArrayList<ApplicationInfo>());
        mContext = c;
        mPackManager = pm;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // get the selected entry
        ApplicationInfo entry = (ApplicationInfo) getItem(position);

        // reference to convertView
        View v = convertView;

        // inflate new layout if null
        if(v == null) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            v = inflater.inflate(R.layout.layout_appinfo, null);
        }

        // load controls from layout resources
        ImageView ivAppIcon = (ImageView)v.findViewById(R.id.ivIcon);
        TextView tvAppName = (TextView)v.findViewById(R.id.tvName);
        TextView tvPkgName = (TextView)v.findViewById(R.id.tvPack);

        // set data to display
        ivAppIcon.setImageDrawable(entry.loadIcon(mPackManager));
        tvAppName.setText(entry.loadLabel(mPackManager));
        tvPkgName.setText(entry.packageName);

        // return view
        return v;
    }
}


回答5:

General procedure

  1. Enable text filtering on your ListView
  2. Change your baseadapter to store two copies of the list, one original, one filtered.
  3. Change all access references in your BaseAdapter to refer to the Filtered list, not the original.
  4. Implement your Filter function in BaseAdapter.

Step 1:
listview.setTextFilterEnabled(true);

Step 2:

public class AppInfoAdapter extends BaseAdapter implements Filterable{
    private List mListAppInfo;
    private List mListAppInfoFiltered;

public AppInfoAdapter(Context c, List list, PackageManager pm) {
    mContext = c;
    mListAppInfo = list;
    mPackManager = pm;
    mPackManagerFiltered = pm; //added line
}

Step 3:

public int getCount() {
    return mListAppInfoFiltered.size();
}
public Object getItem(int position) {
    return mListAppInfoFiltered.get(position);
}
public View getView(int position, View convertView, ViewGroup parent) {
    // get the selected entry
    ApplicationInfo entry = (ApplicationInfo) mListAppInfoFiltered.get(position);

}

Step 4: I am not sure what type your list is, so assuming a list of String:

@Override
public Filter getFilter() {
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();
            if (constraint == null || constraint.length() == 0) {
                //no search, so just return all the data
                results.count = mListAppInfo.size();
                results.values = mListAppInfo;
            } else {//do the search
                List<String> resultsData = new ArrayList<>();
                String searchStr = constraint.toString().toUpperCase();
                for (String s : mListAppInfo)
                        if (s.toUpperCase().contains(searchStr)) resultsData.add(s);
                    results.count = resultsData.size();
                    results.values = resultsData;
                }
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                mListAppInfoFiltered = (ArrayList<MyObject>) results.values;
                notifyDataSetChanged();
            }
        };
    }
}


回答6:

getFilter() can be override in adapters and return the filter object which contains filtered list . There are two key methods in Filter() class; performFiltering and publishResults. The first method performs the filtering in worker thread and the later one return filtered list of objects.

You can refer to sample code below

@Override
public Filter getFilter() {

        return new Filter() {

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                // TODO Auto-generated method stub
                if (results.count == 0) {
                    notifyDataSetInvalidated();
                }else{
                    mListAppInfo = (ArrayList<SampleItem>) results.values;
                    notifyDataSetChanged();
                }
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                // TODO Auto-generated method stub
                FilterResults results = new FilterResults();

                if (constraint == null || constraint.length() == 0) {
                    results.values = mListAppInfo;
                    results.count = mListAppInfo.size();
                }else{
                    ArrayList<SampleItem> filter_items = new ArrayList<>(); 
                    for (SampleItem item : mListAppInfo) {
                        if (item.getItemName().toLowerCase().startsWith(constraint.toString().toLowerCase())) {
                            filter_items.add(item);
                        }
                    }
                    results.values =  filter_items ;
                    results.count = filter_items.size();
                }
                return results;
            }
        };
    }

Hope you find it useful .



回答7:

extend your class with ArrayAdapter,then override methods,and create object of filter class and return with it.