How can I add search method to a list with filterable on the new list adapter?

Viewed 1421

As you can see, below is my class UserAdapter which uses latest ListAdapter which uses DiffUtil callback. In the original Adapter, I could easily implement Filterable and set me coming list to a filterable list, But here I don't seem to have control over selecting the list.

class UserAdapter(private val callback: (Int) -> Unit):
        ListAdapter<User, RecyclerView.ViewHolder>(DIFF_CALLBACK) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val newsBinding = DataBindingUtil.inflate<UserDataBinding>(
                LayoutInflater.from(parent.context),
                R.layout.user_view,
                parent,
                false)

        return UserViewHolder(newsBinding)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as UserViewHolder).bindTo(getItem(position))
        holder.getDataBinding().container.setOnClickListener {
            callback.invoke(position)
        }
        holder.getDataBinding().executePendingBindings()
    }

    companion object {
        val DIFF_CALLBACK= object: DiffUtil.ItemCallback<User>() {

            override fun areItemsTheSame(oldItem: User?, newItem: User?): Boolean {
                return oldItem?.id == newItem?.id
            }

            override fun areContentsTheSame(oldItem: User?, newItem: User?): Boolean {
                return oldItem?.name == newItem?.name
                        && oldItem?.email == newItem?.email
                        && oldItem?.phoneNumber == newItem?.phoneNumber
                        && oldItem?.city == newItem?.city
                        && oldItem?.taluka == newItem?.taluka
                        && oldItem?.state == newItem?.state
                        && oldItem?.category == newItem?.category
                        && oldItem?.profileUrl == newItem?.profileUrl
            }
        }
    }
}
2 Answers

As you can see in the code, I have added the user list as a parameter and created a function which updates the list parameter. Also, instead of assigning filtered list I have called submitList().

class UserAdapter(private var mLstUser: MutableList<User>, private val callback: (Int) -> Unit):
        ListAdapter<User, RecyclerView.ViewHolder>(DIFF_CALLBACK), Filterable {
    var mFilteredList: MutableList<User> = mLstUser

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val newsBinding = DataBindingUtil.inflate<UserDataBinding>(
                LayoutInflater.from(parent.context),
                R.layout.user_view,
                parent,
                false)

        return UserViewHolder(newsBinding)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as UserViewHolder).bindTo(getItem(position))
        holder.getDataBinding().container.setOnClickListener {
            callback.invoke(position)
        }

        holder.getDataBinding().executePendingBindings()
    }

    fun updateList(lstUser: MutableList<User>) {
        mLstUser = lstUser
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(charSequence: CharSequence): Filter.FilterResults {

                val charString = charSequence.toString()

                if (charString.isEmpty()) {
                    mFilteredList = mLstUser
                } else {

                    val filteredList = mLstUser
                            .filter { it.name?.toLowerCase()?.contains(charString)!! }
                            .toMutableList()

                    mFilteredList = filteredList
                }

                val filterResults = Filter.FilterResults()
                filterResults.values = mFilteredList
                return filterResults
            }

            override fun publishResults(charSequence: CharSequence, filterResults: Filter.FilterResults) {
                submitList(filterResults.values as MutableList<User>)
                notifyDataSetChanged()
            }
        }
    }

    companion object {
        val DIFF_CALLBACK= object: DiffUtil.ItemCallback<User>() {

            override fun areItemsTheSame(oldItem: User?, newItem: User?): Boolean {
                return oldItem?.id == newItem?.id
            }

            override fun areContentsTheSame(oldItem: User?, newItem: User?): Boolean {
                return oldItem?.name == newItem?.name
                        && oldItem?.email == newItem?.email
                        && oldItem?.phoneNumber == newItem?.phoneNumber
                        && oldItem?.city == newItem?.city
                        && oldItem?.taluka == newItem?.taluka
                        && oldItem?.state == newItem?.state
                        && oldItem?.category == newItem?.category
                        && oldItem?.profileUrl == newItem?.profileUrl
            }
        }
    }
}

@nayan dhabarde gave us a great kotlin solution and here comes the same solution, but for Java:

I implemented the searchView in my Fragment like below:

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.search_menu, menu);

        SearchManager searchManager = (SearchManager) getActivity().getSystemService(getContext().SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));

        searchView.setQueryHint("Pesquisar...");
        searchView.setMaxWidth(Integer.MAX_VALUE);
        searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if (newText.isEmpty()) {
                    Log.i(TAG, "onQueryTextChange: EMPTY");
                    adapter.getFilter().filter("");
                } else {
                    Log.i(TAG, "onQueryTextChange: search text is "+newText.toString());
                    adapter.getFilter().filter(newText);
                }
                return true;
            }
        });

    }

And below is my adapter:

public class StoreShoppingAdapter extends RecyclerView.Adapter<StoreShoppingAdapter.StoreShoppingViewHolder> implements Filterable {

    private static final String TAG = "debinf StoreShopAda";

    private LocalDatabaseViewModel localDatabaseViewModel;

    private List<StoreEntity> fullList;

    public StoreShoppingAdapter(FragmentActivity fragmentActivity) {
        this.localDatabaseViewModel = ViewModelProviders.of(fragmentActivity).get(LocalDatabaseViewModel.class);

    }

    private static final DiffUtil.ItemCallback<StoreEntity> DIFF_CALLBACK = new DiffUtil.ItemCallback<StoreEntity>() {
        @Override
        public boolean areItemsTheSame(@NonNull StoreEntity oldItem, @NonNull StoreEntity newItem) {
            return false;
        }

        @Override
        public boolean areContentsTheSame(@NonNull StoreEntity oldItem, @NonNull StoreEntity newItem) {
            return false;
        }
    };

    private AsyncListDiffer<StoreEntity> differ = new AsyncListDiffer<StoreEntity>(this, DIFF_CALLBACK);

    @NonNull
    @Override
    public StoreShoppingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_shopping_store, parent, false);
        return new StoreShoppingViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StoreShoppingViewHolder holder, int position) {
        StoreEntity store = differ.getCurrentList().get(position);

        holder.storeName.setText(store.getName());
        holder.storeDate.setText("TimeStamp Here");

        if (store.getImage() != null && !store.getImage().isEmpty()) {
            Picasso.get().load(store.getImage()).networkPolicy(NetworkPolicy.OFFLINE).into(holder.storeImage, new Callback() {
                @Override
                public void onSuccess() {

                }

                @Override
                public void onError(Exception e) {
                    Picasso.get().load(store.getImage()).into(holder.storeImage);
                }
            });
        } else {
            holder.storeImage.setImageResource(android.R.drawable.stat_sys_phone_call_on_hold);
        }

        holder.storeDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                localDatabaseViewModel.deleteStore(store);
            }
        });
    }

    @Override
    public int getItemCount() {
        return differ.getCurrentList().size();
    }

    public void submitList(List<StoreEntity> stores) {
        differ.submitList(stores);
        fullList = new ArrayList<>(stores);
    }

    public List<StoreEntity> getCurrentItemList() {
        return differ.getCurrentList();
    }

    @Override
    public Filter getFilter() {
        return shopFilter;
    }

    private Filter shopFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            List<StoreEntity> filteredList = new ArrayList<>();
            if (constraint == null || constraint.length() == 0) {
                filteredList.addAll(fullList);
            } else {
                String filterPattern = constraint.toString().toLowerCase().trim();

                for (StoreEntity store : fullList) {
                    if (store.getName().toLowerCase().contains(filterPattern)) {
                        filteredList.add(store);
                    }
                }
            }

            FilterResults results = new FilterResults();
            results.values = filteredList;
            return results;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            //differ.getCurrentList().clear();
            //differ.getCurrentList().addAll((List) results.values);
            differ.submitList((List) results.values);
            notifyDataSetChanged();
        }
    };

    public class StoreShoppingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public ImageView storeImage;
        public TextView storeName, storeDate;
        public ImageButton storeDelete;

        public StoreShoppingViewHolder(@NonNull View itemView) {
            super(itemView);

            storeImage = (ImageView) itemView.findViewById(R.id.item_shoppingstore_image);
            storeName = (TextView) itemView.findViewById(R.id.item_shoppingstore_name);
            storeDate = (TextView) itemView.findViewById(R.id.item_shoppingstore_date);
            storeDelete = (ImageButton) itemView.findViewById(R.id.item_shoppingstore_delete);

            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            StoreEntity store = differ.getCurrentList().get(getAdapterPosition());
            Log.i(TAG, "onClick: store clicked is "+store.getName());
            ShopFragmentDirections.ActionShopFragmentToShopcartFragment direction = ShopFragmentDirections.actionShopFragmentToShopcartFragment(store);
            Navigation.findNavController(v).navigate(direction);
        }
    }
}
Related