Android - Implementing search filter to a RecyclerView
in your adapter add new function for update the list
public void updateList(List<DataHolder> list){ displayedList = list; notifyDataSetChanged();}
add textWatcher for search lets say you are using Edittext as search field
searchField.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // TODO Auto-generated method stub } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } @Override public void afterTextChanged(Editable s) { // filter your list from your input filter(s.toString()); //you can use runnable postDelayed like 500 ms to delay search text } });
Simple filter function
void filter(String text){ List<DataHolder> temp = new ArrayList(); for(DataHolder d: displayedList){ //or use .equal(text) with you want equal match //use .toLowerCase() for better matches if(d.getEnglish().contains(text)){ temp.add(d); } } //update recyclerview disp_adapter.updateList(temp);}
With Butterknife
@OnTextChanged(R.id.feature_manager_search)protected void onTextChanged(CharSequence text) { filter(text.toString());}
i have done like this
.XMl file
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><android.support.design.widget.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="wrap_content" app:elevation="1dp" android:theme="@style/AppTheme.AppBarOverlay" android:layout_marginBottom="@dimen/bottom_margin"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:popupTheme="@style/AppTheme.PopupOverlay"/></android.support.design.widget.AppBarLayout><EditText android:id="@+id/etSearch" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Search" android:imeOptions="actionDone" android:singleLine="true"/><android.support.v7.widget.RecyclerView android:id="@+id/rvCategory" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView></LinearLayout>
.JAVA Class file
package com.example.myapplication;import android.content.Context;import android.content.Intent;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.CardView;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.Toolbar;import android.text.Editable;import android.text.TextWatcher;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.Button;import android.widget.EditText;import android.widget.Filter;import android.widget.Filterable;import android.widget.ImageView;import android.widget.TextView;import org.json.JSONObject;import java.util.ArrayList;public class Home extends AppCompatActivity implements ResultCallBack{SQLiteDatabase db;ArrayList<String> alCategorName=new ArrayList<>();RecyclerView rvCategory;EditText etSearch;Category bookadapter;ArrayList<String> alBookInfo;String strURL="http://localhost:21246/api/TreasuryBooks?BookId=1";@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitleTextColor(getResources().getColor(R.color.white)); setSupportActionBar(toolbar);// AsyncTask_WebAPI asyncTask=new AsyncTask_WebAPI(this,strURL,this);// asyncTask.execute(); etSearch=(EditText)findViewById(R.id.etSearch); etSearch.setText(""); DatabaseAccess da= DatabaseAccess.getInstance(this); db=da.open(); alBookInfo=new ArrayList<String>(); Cursor c = db.rawQuery("select BookName from Books", null); int rows = c.getCount(); c.moveToFirst(); for(int i=0;i<rows;i++) { String strBoookName=c.getString(c.getColumnIndex("BookName")); alBookInfo.add(strBoookName); c.moveToNext(); } c.close(); db.close(); rvCategory=(RecyclerView)findViewById(R.id.rvCategory); //Recycler view adapter bookadapter=new Category(this,alBookInfo); rvCategory.setLayoutManager(new LinearLayoutManager(this)); rvCategory.setAdapter(bookadapter); //text change listner etSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { bookadapter.getFilter().filter(s.toString()); } @Override public void afterTextChanged(Editable s) { } });}@Overridepublic void onBackPressed() { Home.this.finish();}@Overrideprotected void onRestart() { super.onRestart(); etSearch.setText("");}@Overridepublic void onResultListener(Object object) { JSONObject jsonObject=(JSONObject)object; try { String strId = jsonObject.getString("id"); String strBookId=jsonObject.getString("BookId"); String strBookName=jsonObject.getString("BookName"); db.rawQuery("insert into Books values('"+strId+"','"+strBookId+"','"+strBookName+"'",null); } catch (Exception e) { e.printStackTrace(); }}
Recyclerview adapter
//Recycler view adapter Class with filterable interface private class Category extends RecyclerView.Adapter<viewHolder> implements Filterable{ ArrayList<String> alBooks=new ArrayList<String>(); Context context; viewHolder holder; public Category() {} public Category(Context context,ArrayList<String> albooks) { this.context=context; this.alBooks=albooks; } @Override public int getItemCount() { return alBooks.size(); } @Override public viewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.home_cardview,parent,false); holder=new viewHolder(v); return holder; } @Override public void onBindViewHolder(viewHolder holder, int position) { holder.tvBookName.setText(alBooks.get(position)); } private Filter fRecords; //return the filter class object @Override public Filter getFilter() { if(fRecords == null) { fRecords=new RecordFilter(); } return fRecords; } //filter class private class RecordFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); //Implement filter logic // if edittext is null return the actual list if (constraint == null || constraint.length() == 0) { //No need for filter results.values = alBookInfo; results.count = alBookInfo.size(); } else { //Need Filter // it matches the text entered in the edittext and set the data in adapter list ArrayList<String> fRecords = new ArrayList<String>(); for (String s : alBookInfo) { if (s.toUpperCase().trim().contains(constraint.toString().toUpperCase().trim())) { fRecords.add(s); } } results.values = fRecords; results.count = fRecords.size(); } return results; } @Override protected void publishResults(CharSequence constraint,FilterResults results) { //it set the data from filter to adapter list and refresh the recyclerview adapter alBooks = (ArrayList<String>) results.values; notifyDataSetChanged(); } } } private class viewHolder extends RecyclerView.ViewHolder { CardView cv; TextView tvBookName; viewHolder(final View itemview) { super(itemview); cv = (CardView) itemview.findViewById(R.id.cardview); tvBookName = (TextView) itemview.findViewById(R.id.categoryName); itemview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent=new Intent(Home.this,Act_Fragment_Container.class); intent.putExtra("BookName", tvBookName.getText()); startActivity(intent); } }); } }
}
add recycler view adapter in same class to avoid confusion....
Implement SearchView.OnQueryTextListener
public class Countries_List extends AppCompatActivity implements SearchView.OnQueryTextListener { //your Model Class List<Countries> dataList, filteredDataList; @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); MenuItem item=menu.findItem(R.id.action_search); SearchView searchView=(SearchView)MenuItemCompat.getActionView(item); searchView.setOnQueryTextListener(this); return true; } @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { filteredDataList = filter(dataList, newText); mAdapter.setFilter(filteredDataList); return true; } public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_search) { return true; } return super.onOptionsItemSelected(item); } private List<Countries> filter(List<Countries> dataList, String newText) { newText=newText.toLowerCase(); String text; filteredDataList=new ArrayList<>(); for(Countries dataFromDataList:dataList){ text=dataFromDataList.getTitle().toLowerCase(); if(text.contains(newText)){ filteredDataList.add(dataFromDataList); } } return filteredDataList; }}
And, in your Adapter Class:
void setFilter(List<Countries> FilteredDataList) { countriesList = FilteredDataList; notifyDataSetChanged(); }