Android - 为 RecyclerView 实现搜索过滤器
Posted
技术标签:
【中文标题】Android - 为 RecyclerView 实现搜索过滤器【英文标题】:Android - Implementing search filter to a RecyclerView 【发布时间】:2017-04-06 20:31:35 【问题描述】:我需要实现一个基于EditText
的搜索功能到我的RecyclerView
。输入时必须过滤列表。
代码如下:
DisplayAdapter.java这是适配器
public class DisplayAdapter extends RecyclerView.Adapter<DisplayAdapter.MyViewHolder>
private List<DataHolder> displayedList;
public class MyViewHolder extends RecyclerView.ViewHolder
public TextView english_d;
public MyViewHolder(View view)
super(view);
english_d = (TextView) view.findViewById(R.id.engword);
public DisplayAdapter(List<DataHolder> displayedList)
this.displayedList = displayedList;
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int position)
// create a layout
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new MyViewHolder(itemView);
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
DataHolder content = displayedList.get(position);
holder.english_d.setText(content.getEnglish());
@Override
public int getItemCount()
return displayedList.size();
DataHolder.java
public class DataHolder
private String english;
public DataHolder()
public DataHolder(String english, String german)
this.english = english;
public String getEnglish()
return english;
public void setEnglish(String english)
this.english = english;
这是我正在使用的片段:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
View view = inflater.inflate(R.layout.activity_two_fragment, container, false);
rv = (RecyclerView) view.findViewById(R.id.list_view_english);
rv.setHasFixedSize(true);
inputSearch = (EditText) view.findViewById(R.id.inputSearch);
rv.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL));
final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(layoutManager);
DisplayContent();
disp_adapter = new DisplayAdapter(displayedList);
rv.setAdapter(disp_adapter);
return view;
【问题讨论】:
github.com/Wrdlbrnft/Searchable-RecyclerView-Demo。请参阅此以获取可搜索的回收站视图,如果您想要详细的教程,请参阅此,studytutorial.in/android-search-to-recyclerview-tutorial 【参考方案1】:在您的适配器中添加更新列表的新功能
public void updateList(List<DataHolder> list)
displayedList = list;
notifyDataSetChanged();
为搜索添加 textWatcher 假设您使用 Edittext 作为搜索字段
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
);
简单的过滤功能
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);
用黄油刀
@OnTextChanged(R.id.feature_manager_search)
protected void onTextChanged(CharSequence text)
filter(text.toString());
【讨论】:
if(d.getEnglish().equal(text)) temp.add(d);
改为if(d.getEnglish().contains(text)) temp.add(d);
谢谢老兄工作得很好,只是 d.getName().toLowerCase().contains(text.toLowerCase()) 会得到明确的结果谢谢
它正在工作。 [***.com/users/3319074/zeroone](@ZeroOne)
这很棒。但我只是想知道这种方法是有效的还是使用 Filterable 是有效的。但是我也没有看到这种方法有任何倒退。谢谢
易于实施。谢谢楼主!【参考方案2】:
我已经这样做了
.XML 文件
<?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_
android:layout_
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_
android:layout_
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_
android:layout_
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<EditText
android:id="@+id/etSearch"
android:layout_
android:layout_
android:hint="Search"
android:imeOptions="actionDone"
android:singleLine="true"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rvCategory"
android:layout_
android:layout_>
</android.support.v7.widget.RecyclerView>
</LinearLayout>
.JAVA类文件
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";
@Override
protected 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)
);
@Override
public void onBackPressed()
Home.this.finish();
@Override
protected void onRestart()
super.onRestart();
etSearch.setText("");
@Override
public 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 适配器
//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);
);
在同一类中添加回收器视图适配器以避免混淆......
【讨论】:
注意android:singleLine="true" 已弃用,建议使用android:maxLines="1"。 嗨@Kishor 它工作得非常好。就在我清除edittext之后,它不会使用原始数据列表进行更新。 @amit ,您必须在发布结果中替换原始列表,维护一个存储每个项目的重复列表,并将其用于执行过滤方法。【参考方案3】:我们可以在 Recyclerview 中应用搜索功能,如下所示。这可以 100% 正常工作。我已经在 button click 上实现了它。可以在EditText的textchangeListner回调方法中使用。
filterIcon.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
String strCHR = homeSearchEdit.getText().toString();
if (homeSearchEdit.getText().toString().length() > 0)
ArrayList<ServiceModel> listNew = new ArrayList<>();
for (int l = 0; l < arrayList.size(); l++)
String serviceName = arrayList.get(l).getServiceName().toLowerCase();
if (serviceName.contains(strCHR.toLowerCase()))
listNew.add(arrayList.get(l));
recyclerView.setVisibility(View.VISIBLE);
adapter = new ServiceAdapter(HomeActivity.this, listNew);
recyclerView.setAdapter(adapter);
else
recyclerView.setVisibility(View.VISIBLE);
adapter = new ServiceAdapter(HomeActivity.this, arrayList);
recyclerView.setAdapter(adapter);
);
哪里 , fliterIcon 是 ImageButton 。 ServiceAdapter 是适配器。
【讨论】:
【参考方案4】:实现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;
并且,在您的适配器类中:
void setFilter(List<Countries> FilteredDataList)
countriesList = FilteredDataList;
notifyDataSetChanged();
【讨论】:
【参考方案5】:在我的例子中,我创建了一个可重复使用的基本适配器。
abstract class RVAdapter<T>(
internal val origValues: MutableList<T>,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable
// use this in base class to access items
protected var values = origValues
private var onNothingFound: (() -> Unit)? = null
override fun getItemCount(): Int = values.size
protected open fun searchCriteria(searchText: String, value: T) : Boolean
return true
fun search(s: String?, onNothingFound: (() -> Unit)? = null )
this.onNothingFound = onNothingFound
filter.filter(s)
override fun getFilter(): Filter
return object : Filter()
private val filterResults = FilterResults()
override fun performFiltering(constraint: CharSequence?): FilterResults
val charSearch = constraint.toString()
if (charSearch.isEmpty())
values = origValues
else
values = ArrayList()
val searchResults = origValues.filter searchCriteria(charSearch, it)
values.addAll(searchResults)
return filterResults.also
it.values = values
override fun publishResults(constraint: CharSequence?, results: FilterResults?)
// no need to use "results" filtered list provided by this method.
values = results?.values as ArrayList<T>
notifyDataSetChanged()
像这样使用它。
class AssetTypeRecyclerViewAdapter(
allValues: MutableList<AssetType>
) : RVAdapter<AssetType>(allValues)
override fun onBindViewHolder(bholder: RecyclerView.ViewHolder, position: Int)
val item = values[position]
val holder = bholder as AssetTypeRecyclerViewAdapter.ViewHolder
...
override fun searchCriteria(searchText: String, value: AssetType) : Boolean
return value.name.toLowerCase(Locale.ROOT).startsWith(searchText)
在片段内部,像往常一样膨胀菜单,并在 onCreateOptionsMenu ...
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater)
menu.clear()
inflater.inflate(R.menu.fragment_list, menu)
val searchView = menu.findItem(R.id.action_search).actionView as SearchView
searchView.maxWidth = Int.MAX_VALUE
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener
override fun onQueryTextSubmit(query: String?): Boolean
assetTypeAdapter.search(query)
return true
override fun onQueryTextChange(newText: String?): Boolean
assetTypeAdapter.search(newText)
return true
)
super.onCreateOptionsMenu(menu, inflater)
【讨论】:
【参考方案6】://simple and easy way
ArrayList<CategoryModel> myList = new ArrayList<>();
for(CategoryModel object: list)
if (object.getName().toLowerCase().contains(str.toLowerCase()))
myList.add(object);
CategoryAdapter adapterM = new CategoryAdapter(myList);
recyclerView.setAdapter(adapterM);
【讨论】:
以上是关于Android - 为 RecyclerView 实现搜索过滤器的主要内容,如果未能解决你的问题,请参考以下文章
如何在android的recyclerview中将最新项目标记为“新”?
Android为TV端助力 listview与recyclerview上下联动
Recyclerview 适配器错误:Android Studio 中的 Arraylist 大小为零
Android,以GridLayoutManager和ImageView为单元格内容的RecyclerView不适合垂直空间