user6677716
user6677716

Reputation:

How to implement search in Recycler View properly

i have a RecyclerView that is taking the data using Realm database, i want to implement a SearchView on the action bar that can search through the items of the RecyclerView, this is my code but i can't seem to make it work.

here's the menu XML

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_search"
    android:icon="@drawable/ic_search_white_24dp"
    android:title="Search"
    app:actionViewClass="android.support.v7.widget.SearchView"
    app:showAsAction="ifRoom"/>
<item android:id="@+id/action_add"
    android:icon="@drawable/ic_add_white_48dp"
    android:title="Add"
    app:showAsAction="ifRoom"/>

the onOptionsItemSelected at the activity

public boolean onOptionsItemSelected(MenuItem item) {
    // Take appropriate action for each action item click
    switch (item.getItemId()) {
        case R.id.action_search:
            // search action
            SearchView searchView=(SearchView)item.getActionView();
            searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    //adapter.getFilter().filter(query);
                    return false;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    //adapter.getFilter().filter(newText);
                    newText=newText.toLowerCase();
                    System.out.println(results);
                    ArrayList<UserInfo> newList=new ArrayList<>();
                    for (UserInfo userInfo : results){
                        String username=userInfo.getUsername().toLowerCase();
                        String password=userInfo.getPassword().toLowerCase();
                        String type=userInfo.getType().toLowerCase();
                        if (username.contains(newText)||password.contains(newText)||type.contains(newText)){
                            newList.add(userInfo);
                        }
                    }
                    adapter.setFilter(newList);
                    return true;
                }
            });
            return true;
        case R.id.action_add:
            addInfo();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

my Adapter class

public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private LayoutInflater layoutInflater;
java.util.List<UserInfo> data= Collections.emptyList();
ArrayList<UserInfo> arrayList;
public Adapter(Context context, java.util.List<UserInfo> data){
    layoutInflater=LayoutInflater.from(context);
    this.data=data;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view=layoutInflater.inflate(R.layout.list_item , parent,false);
    MyViewHolder holder=new MyViewHolder(view);
    return holder;
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    UserInfo current=data.get(position);
    holder.username.setText("Username: "+current.username);
    holder.password.setText("Password: "+current.password);
    holder.type.setText("Type: "+current.type);
}

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

class MyViewHolder extends RecyclerView.ViewHolder{
    TextView username,password,type;
    public MyViewHolder(View itemView) {
        super(itemView);
        username= (TextView) itemView.findViewById(R.id.username_TV);
        password= (TextView) itemView.findViewById(R.id.password_TV);
        type= (TextView) itemView.findViewById(R.id.type_TV);
    }
}
public void setFilter(List<UserInfo> newList){
    arrayList=new ArrayList<>();
    arrayList.addAll(newList);
    notifyDataSetChanged();
}

}

Upvotes: 0

Views: 8526

Answers (6)

Google Devotted
Google Devotted

Reputation: 11

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

        @Override
        public boolean onQueryTextChange(String s) {

            searchKeyword = s;
            s=s.toLowerCase();
            ArrayList<GetGoogleTemplesResponse.ResultsBean> temples=new ArrayList<>();
            for (GetGoogleTemplesResponse.ResultsBean bean : results){
                String templeName=bean.getName().toLowerCase();
                if (templeName.contains(s)){
                    temples.add(bean);
                }
            }
            adapter.setFilter(temples);

            return true;
        }
    });

In adapter

 public void setFilter(List<GetGoogleTemplesResponse.ResultsBean> list){
    beans=new ArrayList<>();
    beans.addAll(list);
    notifyDataSetChanged();
}

Upvotes: 0

Suraj Bahadur
Suraj Bahadur

Reputation: 3928

Hope this answer will help SO.

First of all, you need to create two ArrayList i.e oriList hold original list and modList hold filtered data.

private ArrayList<SingleData> modList;
private ArrayList<SingleData> original;

1. Initialized both of them in the constructer Or create a method in the adapter to set your value like below

public void setEmployees(ArrayList<SingleData> dataList) {
        this.dataList = new ArrayList<>();
        this.dataList = dataList;
        oriDataList = new ArrayList<>();
        oriDataList = (ArrayList<SingleData>) dataList.clone(); 
        notifyDataSetChanged();
    }

**Note:**1. If you don't want to change the original list data then use the clone() method while adding a new list.

2. Created a another method which will handle the search query:

   public void filter(String text) {
        dataList.clear();
        if (text.isEmpty()) {
            dataList.addAll(oriDataList);
        } else {
            text = text.toLowerCase();
            for (SingleData item : oriDataList) {
                if (item.getName().toLowerCase().contains(text)) {
                    dataList.add(item);
                }
            }
        }
        notifyDataSetChanged();
    }

3. Call the search method from the activity or fragment like below

  YOUR_ADAPTER_OBJECT.filter(PASS_SEARCH_TEXT_HERE);

4. SingleData is a pojo class

public class SingleData{

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Upvotes: 0

Richard Kamere
Richard Kamere

Reputation: 799

implement filterable on your adapter class. Override getFilter method.

   @Override
    public Filter getFilter() {

        Filter filter = new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                FilterResults filterResults = new FilterResults();

                if(charSequence == null | charSequence.length() == 0){
                    filterResults.count = getUserModelListFiltered.size();
                    filterResults.values = getUserModelListFiltered;

                }else{
                    String searchChr = charSequence.toString().toLowerCase();

                    List<UserModel> resultData = new ArrayList<>();

                    for(UserModel userModel: getUserModelListFiltered){
                        if(userModel.getUserName().toLowerCase().contains(searchChr)){
                            resultData.add(userModel);
                        }
                    }
                    filterResults.count = resultData.size();
                    filterResults.values = resultData;

                }

                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {

                userModelList = (List<UserModel>) filterResults.values;
                notifyDataSetChanged();

            }
        };
        return filter;
    }

On your main activity, create your searchview and perform filter on querytextchange.

  @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);

        MenuItem menuItem = menu.findItem(R.id.search_view);

        SearchView searchView = (SearchView) menuItem.getActionView();

        searchView.setMaxWidth(Integer.MAX_VALUE);

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

            @Override
            public boolean onQueryTextChange(String newText) {

                usersAdapter.getFilter().filter(newText);
                return true;
            }
        });



        return  true;
    }

you can find the full tutorial and source code here. Recyclerview with searchview

Upvotes: 0

user6677716
user6677716

Reputation:

this is the solution to my problem first at the onCreateOptionsMenu add this

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main_actions, menu);
    MenuItem search=menu.findItem(R.id.action_search);

    //this 2 lines
    SearchView searchView=(SearchView)search.getActionView();
    search(searchView);

    return super.onCreateOptionsMenu(menu);
}

the search method

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

        @Override
        public boolean onQueryTextChange(String newText) {
            newText=newText.toLowerCase();
            ArrayList<UserInfo> newList=new ArrayList<>();
            for (UserInfo userInfo : results){
                String username=userInfo.getUsername().toLowerCase();
                String password=userInfo.getPassword().toLowerCase();
                String type=userInfo.getType().toLowerCase();
                if (username.contains(newText)||password.contains(newText)||type.contains(newText)){
                    newList.add(userInfo);
                }
            }
            adapter.setFilter(newList);
            return true;
        }
    });
}

the setFilter method that is written at the adapter

public void setFilter(List<UserInfo> newList){
    data=new ArrayList<>();
    data.addAll(newList);
    notifyDataSetChanged();
}

Upvotes: 2

Anand Diamond
Anand Diamond

Reputation: 339

I hope this answer helps you Mahmoud Omara

Hi Mahmoud Omara are you using realm database.

In Recyclerview adapter implement Filterable then generate @Override Method getFilter.

Then implement your own filter look like i have change your adapter

public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder>  implements Filterable {
private LayoutInflater layoutInflater;
java.util.List<UserInfo> data= Collections.emptyList();
public Adapter(Context context, java.util.List<UserInfo> data){
    layoutInflater=LayoutInflater.from(context);
    this.data=data;
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view=layoutInflater.inflate(R.layout.list_item , parent,false);
    MyViewHolder holder=new MyViewHolder(view);
    return holder;
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    UserInfo current=data.get(position);
    holder.username.setText("Username: "+current.username);
    holder.password.setText("Password: "+current.password);
    holder.type.setText("Type: "+current.type);
}

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

class MyViewHolder extends RecyclerView.ViewHolder{
    TextView username,password,type;
    public MyViewHolder(View itemView) {
        super(itemView);
        username= (TextView) itemView.findViewById(R.id.username_TV);
        password= (TextView) itemView.findViewById(R.id.password_TV);
        type= (TextView) itemView.findViewById(R.id.type_TV);
    }
}

@Override
public Filter getFilter() {
    return new MyNamesFilter();
}
private class MyNamesFilter
        extends Filter {


    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        return new FilterResults();
    }

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

        //myRealm is your realm object
        //contactName is in your database column name  replace your table name column

        RealmResults<Contact> contactArrayLisst = myRealm.where(Contact.class)
                .beginGroup()
                .contains("contactName", constraint.toString(), Case.INSENSITIVE)
                .endGroup()
                .findAll();
        data.clear();
        data.addAll(contactArrayLisst);
        notifyDataSetChanged();


    }
}
}

Then changes your activity class onQueryTextChange Method

look like this

 adapter.getFilter().filter(s);

Upvotes: 2

Akhil
Akhil

Reputation: 6697

It seems like you are using arrayList intstead of data in your setFilter method,use data instead

Upvotes: 0

Related Questions