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之RecyclerView介绍

Android为TV端助力 listview与recyclerview上下联动

Recyclerview 适配器错误:Android Studio 中的 Arraylist 大小为零

Android,以GridLayoutManager和ImageView为单元格内容的RecyclerView不适合垂直空间

Android RecyclerView使用方法详解