Search code examples
androidlistviewandroid-arrayadapterandroid-listadapter

My custom search view is not working correctly, not showing original list when search input is null


I am trying to implement a custom searchable spinner in my activity but something is wrong in the code and I can't find it. Look at the screenshots below to understand my problem.

snap1 Here is the original listview with three items and no search filter added yet

snap2 Here is the listview after the search filter

snap3 Here is the listview after removing the search.

So as you can see in third screenshot the listview is not showing all three items after search filter is cleared. Can you see what mistake I am making in my code?

ListAdapter.java

public class ListAdapter extends ArrayAdapter<SpinnerItem> {

    private List<SpinnerItem> items;
    private Context context;
    private Filter filter;
    public ListAdapter(@NonNull Context context, int resource, List<SpinnerItem> items) {
        super(context, resource);
        this.context = context;
        this.items = items;
        this.filter = new CustomItemsFilter();
    }

    @Nullable
    @Override
    public SpinnerItem getItem(int position) {
        return items.get(position);
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rootView = inflater.inflate(R.layout.list_item,parent,false);
        TextView textView = rootView.findViewById(R.id.itemTitle);
        TextView textView1 = rootView.findViewById(R.id.itemSubTitle);
        TextView textView2 = rootView.findViewById(R.id.itemSign);

        textView.setText(items.get(position).getTitle());
        textView1.setText(items.get(position).getSubTitle());
        textView2.setText(items.get(position).getSign());
        return rootView;
    }

    @NonNull
    @Override
    public Filter getFilter() {

        if(filter == null) {
            filter = new CustomItemsFilter();
        }
        return filter;
    }

    private class CustomItemsFilter extends Filter {

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            FilterResults filterResults = new FilterResults();

            if(constraint != null && constraint.toString().length() > 0) {
                constraint = constraint.toString().toLowerCase();
                List<SpinnerItem> allItems = new ArrayList<SpinnerItem>();
                List<SpinnerItem> filterItems = new ArrayList<SpinnerItem>();
                synchronized (this) {
                    allItems.addAll(items);
                }
                for (int i = 0; i < allItems.size(); ++i) {
                    SpinnerItem item = allItems.get(i);
                    if(item.getTitle().contains(constraint) || item.getSubTitle().contains(constraint) || item.getSign().contains(constraint)) {
                        filterItems.add(item);
                    }
                }
                filterResults.count = filterItems.size();
                filterResults.values = filterItems;
            }
            else {
                synchronized (this) {
                    filterResults.values = items;
                    filterResults.count = items.size();
                }
            }
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {

            if(results.count > 0) {
                items = (List<SpinnerItem>) results.values;
                notifyDataSetChanged();
            }
        }

    }
}

SpinnerDialog.java

public class SpinnerDiaglog implements SearchView.OnCloseListener, SearchView.OnQueryTextListener{

     private List<SpinnerItem> items;
     private OnSpinnerItemClick onSpinnerItemClick;
     private Activity context;
     private SearchView searchView;
     private DialogInterface.OnClickListener onClickListener;
     private ListAdapter adapter;
     private OnSearchTextChanged onSearchTextChanged;
     private ListView listView;
     private AlertDialog dialog;

    public SpinnerDiaglog() {
    }

    public SpinnerDiaglog(Activity context, List<SpinnerItem> items) {
         this.context = context;
         this.items = items;
     }

    public void onCreateDialog() {
        View rootView = context.getLayoutInflater().inflate(R.layout.spinner_dialog,null);
        //setData(rootView);
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
        alertDialog.setView(rootView);
        alertDialog.setPositiveButton("CLOSE",onClickListener);
        alertDialog.setTitle("Select Item");

        SearchManager searchManager = (SearchManager) context.getSystemService(Context
                .SEARCH_SERVICE);

        searchView = rootView.findViewById(R.id.search);
        searchView.setSearchableInfo(searchManager.getSearchableInfo(context.getComponentName()));
        searchView.setOnQueryTextListener(this);
        searchView.setOnCloseListener(this);
        searchView.setIconifiedByDefault(false);
        searchView.clearFocus();

        listView = rootView.findViewById(R.id.listItems);
        adapter = new ListAdapter(context,R.layout.list_item,items);
        listView.setAdapter(adapter);

        dialog = alertDialog.create();
        dialog.show();

        listView.setOnItemClickListener((parent, view, position, id) -> {
            onSpinnerItemClick.onClick(adapter.getItem(position),position);
            dialog.dismiss();
        });
    }

    public void setOnSpinnerItemClickListener(OnSpinnerItemClick onSpinnerItemClick) {
        this.onSpinnerItemClick = onSpinnerItemClick;
    }
    public void setOnSearchTextChangedListener(OnSearchTextChanged onSearchTextChanged) {
        this.onSearchTextChanged = onSearchTextChanged;
    }


    @Override
    public boolean onQueryTextSubmit(String query) {
         searchView.clearFocus();
        return false;
    }

    @Override
    public boolean onQueryTextChange(String newText) {

         if(TextUtils.isEmpty(newText)) {
             ((ListAdapter) listView.getAdapter()).getFilter().filter(null);
         }
         else {
             ((ListAdapter) listView.getAdapter()).getFilter().filter(newText);
         }
        if (null != onSearchTextChanged) {
            onSearchTextChanged.onSearchTextChanged(newText);
        }
        return true;
    }

    public interface OnSpinnerItemClick {

        void onClick(SpinnerItem item, int position);
    }

    public interface OnSearchTextChanged {
        void onSearchTextChanged(String strText);
    }


    @Override
    public boolean onClose() {
        return false;
    }


}

MainActivity.java

public class MainActivity2 extends AppCompatActivity {

    private SpinnerDiaglog spinnerDiaglog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        Button button = findViewById(R.id.show);

        List<SpinnerItem> items = new ArrayList<>();
        items.add(new SpinnerItemImp("Title","subTitle","sign"));
        items.add(new SpinnerItemImp("aitle","subTitle","sign"));
        items.add(new SpinnerItemImp("Tiastle","subTitle","sign"));
        button.setOnClickListener(v -> {

            spinnerDiaglog = new SpinnerDiaglog(MainActivity2.this,items);
            spinnerDiaglog.onCreateDialog();

        });
    }
}

SpinnerItemImp.java

public class SpinnerItemImp implements SpinnerItem {

    private String title;
    private String subTitle;
    private String sign;

    public SpinnerItemImp(String title, String subTitle, String sign) {
        this.title = title;
        this.subTitle = subTitle;
        this.sign = sign;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public String getSubTitle() {
        return subTitle;
    }

    @Override
    public String getSign() {
        return sign;
    }
}

Solution

  • You are keeping a single list items which is getting modified when you apply the filter for first time . So on next filter you are actually filtering from the filtered list not the actual list .

    You need to maintain a Complete list separately and apply filter on it not on the filtered List . Below is an example:

    public class ListAdapter extends ArrayAdapter<SpinnerItem> {
        private List<SpinnerItem> items;
        private List<SpinnerItem> allItems;
        private Context context;
        private Filter filter;
        public ListAdapter(@NonNull Context context, int resource, List<SpinnerItem> items) {
            super(context, resource);
            this.context = context;
            this.items = items;
            this.filter = new CustomItemsFilter();
            allItems=new ArrayList<>(items);
        }
    
        @Override
        public SpinnerItem getItem(int position) {
            return items.get(position);
        }
    
        @Override
        public int getCount() {
            return items.size();
        }
    
        @NonNull
        @Override
        public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View rootView = inflater.inflate(R.layout.list_item,parent,false);
            return rootView;
        }
    
        @NonNull
        @Override
        public Filter getFilter() {
            if(filter == null) {
                filter = new CustomItemsFilter();
            }
            return filter;
        }
    
        private class CustomItemsFilter extends Filter {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                items.clear();
                FilterResults filterResults = new FilterResults();
                constraint = constraint.toString().trim().toLowerCase();
                if (constraint.length() > 0) {
                    for (int i = 0; i < allItems.size(); ++i) {
                        SpinnerItem item = allItems.get(i);
                        if (item.getTitle().contains(constraint) || item.getSubTitle().contains(constraint) || item.getSign().contains(constraint)) {
                            items.add(item);
                        }
                    }
                } else {
                    items.addAll(allItems);
                }
                filterResults.values = items;
                filterResults.count = items.size();
                return filterResults;
            }
    
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
               
                    items = (List<SpinnerItem>) results.values;
                    notifyDataSetChanged();
               
            }
        }
    }