本文介绍了如何与一个搜索查看过滤RecyclerView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我试图实施搜索查看从支持库。我希望用户可以使用搜索查看在过滤列表电影 A RecyclerView 。我按照一些教程,到目前为止,我已经添加了搜索查看到动作条,但我真的不知道从哪里何去何从。我看到了几个例子,但他们没有显示出结果,你开始输入。这是我的 MainActivity : 公共类MainActivity扩展ActionBarActivity { RecyclerView mRecyclerView; RecyclerView.LayoutManager mLayoutManager; RecyclerView.Adapter mAdapter; @覆盖 保护无效的onCreate(包savedInstanceState){ super.onCreate(savedInstanceState); 的setContentView(R.layout.activity_recycler_view); mRecyclerView =(RecyclerView)findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(真正的); mLayoutManager =新LinearLayoutManager(本); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter =新CardAdapter(){ @覆盖 公共过滤用getFilter(){ 返回null; } }; mRecyclerView.setAdapter(mAdapter); } @覆盖 公共布尔onCreateOptionsMenu(功能菜单){ //充气菜单;这增加了项目操作栏,如果它是present。 。getMenuInflater()膨胀(R.menu.menu_main,菜单); SearchManager searchManager =(SearchManager)getSystemService(Context.SEARCH_SERVICE); 搜索查看搜索查看=(搜索查看)menu.findItem(R.id.menu_search).getActionView(); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); 返回true; } @覆盖 公共布尔onOptionsItemSelected(菜单项项){ //处理动作栏项目点击这里。将操作栏 //自动在主/向上按钮操作的点击,只要 //你在AndroidManifest.xml中指定一个父活动。 INT的id = item.getItemId(); // noinspection SimplifiableIfStatement 如果(ID == R.id.action_settings){ 返回true; } 返回super.onOptionsItemSelected(项目); }}这是我的适配器: 公共抽象类CardAdapter扩展RecyclerView.Adapter&LT; CardAdapter.ViewHolder&GT;实现过滤的{ 名单&LT;电影&GT; mItems; 公共CardAdapter(){ 超(); mItems =新的ArrayList&LT;电影&GT;(); 动画电影=新电影(); movie.setName(蜘蛛侠); movie.setRating(92); mItems.add(电影); 电影=新电影(); movie.setName(毁灭战士3); movie.setRating(91); mItems.add(电影); 电影=新电影(); movie.setName(变形金刚); movie.setRating(88); mItems.add(电影); 电影=新电影(); movie.setName(变形金刚2); movie.setRating(87); mItems.add(电影); 电影=新电影(); movie.setName(变形金刚3); movie.setRating(86); mItems.add(电影); 电影=新电影(); movie.setName(诺亚); movie.setRating(86); mItems.add(电影); 电影=新电影(); movie.setName(铁人); movie.setRating(86); mItems.add(电影); 电影=新电影(); movie.setName(铁人2); movie.setRating(86); mItems.add(电影); 电影=新电影(); movie.setName(铁人三项3); movie.setRating(86); mItems.add(电影); } @覆盖 公共ViewHolder onCreateViewHolder(ViewGroup中的ViewGroup,int i)以{ 视图V = LayoutInflater.from(viewGroup.getContext())膨胀(R.layout.recycler_view_card_item,ViewGroup中,假的)。 返回新ViewHolder(V); } @覆盖 公共无效onBindViewHolder(ViewHolder viewHolder,int i)以{ 动画视频= mItems.get(我); viewHolder.tvMovie.setText(movie.getName()); viewHolder.tvMovieRating.setText(movie.getRating()); } @覆盖 公众诠释getItemCount(){ 返回mItems.size(); } 类ViewHolder扩展RecyclerView.ViewHolder { 公众的TextView tvMovie; 公众的TextView tvMovieRating; 公共ViewHolder(查看ItemView控件){ 超(ItemView控件); tvMovie =(TextView中)itemView.findViewById(R.id.movieName); tvMovieRating =(TextView中)itemView.findViewById(R.id.movieRating); } }}解决方案 简介因为它不是真正明确形成与究竟你有困难,我写了这个关于如何实现此功能快速演练你的问题,如果您还有问题随时问。我有一切,我在这里谈论这个 GitHub的信息库 工作的例子。结果应该看起来是这样的: 1)设置了搜索查看在你的菜单XML只是增加一个项目,然后设置 actionViewClass 来``。由于您使用的是支持库,你必须使用支持库的空间来设置 actionViewClass 属性。你的菜单XML应该是这个样子: &LT;菜单的xmlns:机器人=http://schemas.android.com/apk/res/android 的xmlns:程序=http://schemas.android.com/apk/res-auto&GT; &LT;项目机器人:ID =@ + ID / ACTION_SEARCH 机器人:标题=@字符串/ ACTION_SEARCH 应用程序:actionViewClass =android.support.v7.widget.SearchView 应用程序:showAsAction =总是/&GT;&LT; /菜单&gt; 在你的片段或活动你有夸大像往常这个菜单中的XML,那么你可以看看的菜单项包含搜索查看和实施 OnQueryTextListener 其中我们要使用侦听更改文本输入到搜索查看: @覆盖公共无效onCreateOptionsMenu(功能菜单,MenuInflater充气){ inflater.inflate(R.menu.menu_main,菜单); 最终的菜单项项= menu.findItem(R.id.action_search); 最后搜索查看搜索查看=(搜索查看)MenuItemCompat.getActionView(项目); searchView.setOnQueryTextListener(本);}@覆盖公共布尔onQueryTextChange(查询字符串){ //这里就是我们要实现我们的过滤逻辑 返回false;}@覆盖公共布尔onQueryTextSubmit(查询字符串){ 返回false;}而现在的搜索查看是随时可以使用。我们将在 onQueryTextChange后来就实现了过滤器的逻辑()一旦我们完成实施适配器。 2)设置了适配器首先,这是模型类我将使用这个例子: 公共类ExampleModel { 私人最终字符串行文字; 公共ExampleModel(字符串文本){ 多行文字=文本; } 公共字符串的getText(){ 返回行文字; }}这只是你的,这将显示在 RecyclerView 一文的基本模式。这是布局,我要用来显示文字: &LT; XML版本=1.0编码=UTF-8&GT?;&LT;的FrameLayout 的xmlns:机器人=http://schemas.android.com/apk/res/android 机器人:layout_width =match_parent 机器人:layout_height =WRAP_CONTENT 机器人:可点击=真 机器人:后台=&gt;中ATTR / selectableItemBackground? &LT;的TextView 机器人:ID =@ + ID / tvText 机器人:layout_width =match_parent 机器人:layout_height =WRAP_CONTENT 机器人:填充=8DP/&GT;&LT; /的FrameLayout&GT;而 ViewHolder 我使用这样的容貌: 公共类ExampleViewHolder扩展RecyclerView.ViewHolder { 私人最终的TextView tvText; 公共ExampleViewHolder(查看ItemView控件){ 超(ItemView控件); tvText =(TextView中)itemView.findViewById(R.id.tvText); } 公共无效绑定(ExampleModel模型){ tvText.setText(model.getText()); }}再没有什么特别的。它只是规定从文字 ExampleModel 到的TextView 在我们的布局。现在,我们终于可以来到真正有趣的部分:编写适配器。我要跳过的适配器和我,而不是将精力集中在其相关的部件搜索查看。这是适配器的基本实现我开始了: 公共类ExampleAdapter扩展RecyclerView.Adapter&LT; ExampleViewHolder&GT; { 私人最终LayoutInflater mInflater; 私人列表&LT; ExampleModel&GT; mModels; 公共ExampleAdapter(上下文的背景下,名单,其中,ExampleModel&GT;型号){ mInflater = LayoutInflater.from(上下文); mModels =新的ArrayList&LT;&GT;(型号); } @覆盖 公共ExampleViewHolder onCreateViewHolder(ViewGroup中的父母,INT viewType){ 最后查看ItemView控件= mInflater.inflate(R.layout.item_example,父母,假); 返回新ExampleViewHolder(ItemView控件); } @覆盖 公共无效onBindViewHolder(ExampleViewHolder持有人,INT位置){ 最终ExampleModel模型= mModels.get(位置); holder.bind(模型); } @覆盖 公众诠释getItemCount(){ 返回mModels.size(); } 公共无效setModels(名单&LT; ExampleModel&GT;型号){ mModels =新的ArrayList&LT;&GT;(型号); }}基本上,这是所有你需要做这项工作。您可以过滤在 onQueryTextChange)项目(并使用 setModels()方法来改变车型在适配器,然后调用 notifyDatasetChanged()在适配器更新 RecyclerView 。或者至少那会是同一个的ListView 做到这一点的方式。但 RecyclerView 带来了全新的元素表中,我们可以利用脱!出的现成的项目动画的支持 3)获取适配器准备好项目的动画我们必须做的第一件事是定义了三种辅助方法,这使我们能够添加,删除或周围的适配器移动项目。这些方法会自动调用所需的通知... 方法来触发该项目的动画,伴随它。 公共ExampleModel removeItem(INT位置){ 最终ExampleModel模型= mModels.remove(位置); notifyItemRemoved(位置); 回归模型;}公共无效的addItem(INT位置,ExampleModel模型){ mModels.add(位置,型号); notifyItemInserted(位置);}公共无效移动选项(INT fromPosition,诠释toPosition){ 最终ExampleModel模型= mModels.remove(fromPosition); mModels.add(toPosition,型号); notifyItemMoved(fromPosition,toPosition);}再没有什么特别的。我们只是修改对象的内部列表里的适配器通过删除,添加或移动的物体,一旦我们完成了所谓的通知... 方法。现在我们要实现,这将在列表目前在适配器显示的对象之间动画的方法该过滤列表,我们将提供给该方法。 公共无效animateTo(名单&LT; ExampleModel&GT;型号){ applyAndAnimateRemovals(型号); applyAndAnimateAdditions(型号); applyAndAnimateMovedItems(型号);}包含在 animateTo的三种方法()做所有的工作在这里,但顺序是非常重要的!关于动画这样的多个项目中最困难的部分是跟踪指数。我的意思是,如果比如你添加一个项目,然后下面你加入这个项目的所有项目都向下移动。同样,如果你删除它下面所有的项目都搬到了一个项目。这是一个大问题,因为所有的通知...()方法,从而启动该项目的动画需要一个项目的索引。如果你不小心添加或删除项目,然后尝试调用通知... 方法,你要结束了怪异出问题的动画,甚至是 ArrayIndexOutOfBoundsException异常。我们做两件事情,大大简化了这个问题对我们来说:我们有我们中的三台助手从上面我们可以用它来添加,删除或移动的物品,他们会自动调用正确的通知... 方法的方法。所以我们不必担心动画了。我们可以完全专注于改进内部列出适配器。的我们修改内部的适配器列表时,定义操作的具体顺序。正如我上面提到的,我们提供的过滤列表到适配器通过将其插入 animateTo()方法。第一步是删除不经过滤的列表中不存在了所有项目。接下来的一步是添加这并没有在原来的列表存在,但做过滤列表中的所有项目。最后一步是将两个列表中存在的所有项目。这正是在 animateTo的三种方法()做的。现在我们要看看这三个方法的实现。让我们开始为了与 applyAndAnimateRemovals(): 私人无效applyAndAnimateRemovals(名单&LT; ExampleModel&GT; newModels){ 对于(INT I = mModels.size() - 1; I&GT; = 0;我 - ){ 最终ExampleModel模型= mModels.get(我); 如果(!newModels.contains(模型)){ removeItem(ⅰ); } }}你可以通过内部列表的适配器向后检查看到这个方法迭代如果每个产品包含在新的过滤列表。如果不是,它调用 removeItem()。我们迭代向后的原因是为了避免不必跟踪的偏移。如果你删除一个项目下的所有项目上移。如果通过对目录遍历从下往上那么只有你已经迭代移动的物品。现在让我们看看 applyAndAnimateAdditions(): 私人无效applyAndAnimateAdditions(名单&LT; ExampleModel&GT; newModels){ 对于(INT I = 0,数= newModels.size(); I&LT;计数;我++){ 最终ExampleModel模型= newModels.get(我); 如果(!mModels.contains(模型)){ 的addItem(我,型号); } }},它基本上是()但不是通过内部列表的迭代同样的事情, applyAndAnimateRemovals的适配器它通过过滤列表并检查迭代如果该项目存在内部列表。如果没有,它调用的addItem()。最后让我们看看 applyAndAnimateMovedItems(): 私人无效applyAndAnimateMovedItems(名单&LT; ExampleModel&GT; newModels){ 对于(INT toPosition = newModels.size() - 1; toPosition&GT; = 0; toPosition--){ 最终ExampleModel模型= newModels.get(toPosition); 最终诠释fromPosition = mModels.indexOf(模型); 如果(fromPosition&GT; = 0&安培;&安培; fromPosition = toPosition!){ 移动选项(fromPosition,toPosition); } }} 此方法实现超过previous两个更复杂的逻辑。它本质上是 applyAndAnimateRemovals的组合()和 applyAndAnimateAdditions(),但与一捻。你必须认识到,在这一点上 applyAndAnimateRemovals()和 applyAndAnimateAdditions()已被调用。所以,我们取消所有需要被删除的项目,我们加入需要被添加的所有新项目。因此,内部列表的适配器和过滤列表包含完全相同的项目,但它们可以以不同的顺序。什么 applyAndAnimateMovedItems()现在所做的就是通过过滤列表向后遍历和查找每个项目中的索引内部列表。如果它检测到它调用的指数差移动选项()带来的内部列表的适配器符合过滤的列表。和与我们的适配器已完成。它现在可以显示的项目,并自动触发相应的动画,我们筛选对象的列表。唯一缺少的,现在是连接搜索查看到 RecyclerView !有一件事我还是应该提到的是,虽然这是一个非常简单的方法,以动画它是迄今为止并不是最有效的一个项目。但对于大多数使用情况下,它应该足够越来越一旦你了解它的要点可以非常快速地实现它。 4)实施过滤逻辑在你的问题引起了我的眼睛一件事是,你保持你想在适配器直接显示的项目列表。而适配器当然必须有一个列表项目内部,你不应该完全保持列表在那里。该适配器 - 顾名思义 - 应该只是把对象到浏览。你给它一个列表的对象和 RecyclerView 变一个查看重presenting每个对象。什么适配器绝对是不负责的创建者对象。要实现,我们首先要确定所有可能的对象 A 列表过滤逻辑。正如我上面提到的,我们会做的不是适配器内部,但外面的片段或活动包含 RecyclerView 。基本实现(在我的情况下,为片段)看起来是这样的: 私人RecyclerView mRecyclerView;私人ExampleAdapter mAdapter;私人列表&LT; ExampleModel&GT; mModels;@覆盖公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,捆绑savedInstanceState){ 最终的视图中查看= inflater.inflate(R.layout.fragment_main,集装箱,假); mRecyclerView =(RecyclerView)view.findViewById(R.id.recyclerView); 返回查看;}@覆盖公共无效onViewCreated(查看视图,捆绑savedInstanceState){ super.onViewCreated(查看,savedInstanceState); setHasOptionsMenu(真正的); mRecyclerView.setLayoutManager(新LinearLayoutManager(getActivity())); mModels =新的ArrayList&LT;&GT;(); 对于(字符串电影:电影){ mModels.add(新ExampleModel(电影)); } mAdapter =新ExampleAdapter(getActivity(),mModels); mRecyclerView.setAdapter(mAdapter);}再没有什么特别的。我们设置 RecyclerView 和实例化适配器。我们创建了一个列表项的并存储在一个领域。这份名单将是我们的参考。它包含我们将过滤与搜索查看所有可能的项目。在上面的例子中电影是的String [] 我定义它包含我的测试数据。现在,我们可以回去 onQueryTextChange()我们前面,并开始实施过滤逻辑定义的: @覆盖公共布尔onQueryTextChange(查询字符串){ 最后的名单,其中,ExampleModel&GT; filteredModelList =过滤器(mModels,查询); mAdapter.animateTo(filteredModelList); mRecyclerView.scrollToPosition(0); 返回true;} 这是一次pretty的直线前进。我们调用方法过滤器(),并通过在我们的对象引用列表和查询字符串。然后,我们称之为 animateTo()在适配器,并通过过滤列表按过滤器()。我们也有叫 scrollToPosition(0)在 RecyclerView ,以确保用户总能看到所有的项目进行搜索时,的东西。否则, RecyclerView 可能停留在滚动向下的位置,同时过滤并随后隐藏几个项目。滚动顶端确保了更好的用户体验,同时搜索。唯一剩下现在要做的是落实过滤器()本身: 私人列表&LT; ExampleModel&GT;过滤器(表&LT; ExampleModel&GT;模型,查询字符串){ 查询= query.toLowerCase(); 最后的名单,其中,ExampleModel&GT; filteredModelList =新的ArrayList&LT;&GT;(); 对于(ExampleModel型号:型号){ 最后字符串文本= model.getText()与toLowerCase()。 如果(text.contains(查询)){ filteredModelList.add(模型); } } 返回filteredModelList;} 我们在这里做的第一件事是调用与toLowerCase()的查询字符串。我们不希望我们的搜索功能是区分大小写的,通过调用与toLowerCase()在所有我们比较我们可以确保我们返回的情况下相同的结果而不管。字符串过滤器()通过所有的车型基本上只是迭代的列表我们传递给它并检查查询字符串包含在该模型的文本。如果它是那么的模型将被添加到过滤列表。这是pretty的太多了!如果你运行你的应用程序,现在你应该可以在 RecyclerView 与搜索查看过滤数据,这整个事情运行在升级Froyo(Android 2.2系统,API 7级)及以上!并开始蜂巢(Android的3.0,API级别11)的所有更改在 RecyclerView 将自动动画显示的数据集!我知道这是一个很详细的说明这可能使得这整个事情看起来更加复杂,比它确实是。我建议你看一下在 GitHub的信息库的工作示例我上面的话挂您无法理解这件事的一个具体方面。I am trying to implement the SearchView from the support library. I want the user to be to use the SearchView to filter a List of movies in a RecyclerView.I have followed a few tutorials so far and I have added the SearchView to the ActionBar, but I am not really sure where to go from here. I have seen a few examples but none of them show results as you start typing.This is my MainActivity:public class MainActivity extends ActionBarActivity { RecyclerView mRecyclerView; RecyclerView.LayoutManager mLayoutManager; RecyclerView.Adapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler_view); mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new CardAdapter() { @Override public Filter getFilter() { return null; } }; mRecyclerView.setAdapter(mAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); return true; } @Override 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_settings) { return true; } return super.onOptionsItemSelected(item); }}And this is my Adapter:public abstract class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> implements Filterable { List<Movie> mItems; public CardAdapter() { super(); mItems = new ArrayList<Movie>(); Movie movie = new Movie(); movie.setName("Spiderman"); movie.setRating("92"); mItems.add(movie); movie = new Movie(); movie.setName("Doom 3"); movie.setRating("91"); mItems.add(movie); movie = new Movie(); movie.setName("Transformers"); movie.setRating("88"); mItems.add(movie); movie = new Movie(); movie.setName("Transformers 2"); movie.setRating("87"); mItems.add(movie); movie = new Movie(); movie.setName("Transformers 3"); movie.setRating("86"); mItems.add(movie); movie = new Movie(); movie.setName("Noah"); movie.setRating("86"); mItems.add(movie); movie = new Movie(); movie.setName("Ironman"); movie.setRating("86"); mItems.add(movie); movie = new Movie(); movie.setName("Ironman 2"); movie.setRating("86"); mItems.add(movie); movie = new Movie(); movie.setName("Ironman 3"); movie.setRating("86"); mItems.add(movie); } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false); return new ViewHolder(v); } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { Movie movie = mItems.get(i); viewHolder.tvMovie.setText(movie.getName()); viewHolder.tvMovieRating.setText(movie.getRating()); } @Override public int getItemCount() { return mItems.size(); } class ViewHolder extends RecyclerView.ViewHolder{ public TextView tvMovie; public TextView tvMovieRating; public ViewHolder(View itemView) { super(itemView); tvMovie = (TextView)itemView.findViewById(R.id.movieName); tvMovieRating = (TextView)itemView.findViewById(R.id.movieRating); } }} 解决方案 IntroductionSince it is not really clear form your question with what exactly you are having trouble I wrote up this quick walkthrough about how to implement this feature, if you still have questions feel free to ask.I have a working example of everything I am talking about here in this GitHub Repository.The result should looks something like this:1) Setting up the SearchViewIn your menu xml just add an item and set the actionViewClass to ``. Since you are using the support library you have to use the namespace of the support library to set the actionViewClass attribute. Your menu xml should look something like this:<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:title="@string/action_search" app:actionViewClass="android.support.v7.widget.SearchView" app:showAsAction="always"/></menu>In your Fragment or Activity you have to inflate this menu xml like usual, then you can look for the MenuItem which contains the SearchView and implement the OnQueryTextListener which we are going to use to listen for changes to the text entered into the SearchView:@Overridepublic void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_main, menu); final MenuItem item = menu.findItem(R.id.action_search); final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item); searchView.setOnQueryTextListener(this);}@Overridepublic boolean onQueryTextChange(String query) { // Here is where we are going to implement our filter logic return false;}@Overridepublic boolean onQueryTextSubmit(String query) { return false;}And now the SearchView is ready to be used. We will implement the filter logic later on in onQueryTextChange() once we are finished implementing the Adapter.2) Setting up the AdapterFirst and foremost this is the model class I am going to use for this example: public class ExampleModel { private final String mText; public ExampleModel(String text) { mText = text; } public String getText() { return mText; }}It's just your basic model which will display a text in the RecyclerView. This is the layout I am going to use to display the text: <?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:background="?attr/selectableItemBackground"> <TextView android:id="@+id/tvText" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp"/></FrameLayout>And the ViewHolder I am using looks like this:public class ExampleViewHolder extends RecyclerView.ViewHolder { private final TextView tvText; public ExampleViewHolder(View itemView) { super(itemView); tvText = (TextView) itemView.findViewById(R.id.tvText); } public void bind(ExampleModel model) { tvText.setText(model.getText()); }}Again nothing special. It just sets the text from the ExampleModel to the TextView in our layout.Now we can finally come to the really interesting part: Writing the Adapter. I am going to skip over the basic implementation of the Adapter and am instead going to concentrate on the parts which are relevant for the SearchView. This is the basic implementation of the Adapter I started out with:public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> { private final LayoutInflater mInflater; private List<ExampleModel> mModels; public ExampleAdapter(Context context, List<ExampleModel> models) { mInflater = LayoutInflater.from(context); mModels = new ArrayList<>(models); } @Override public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View itemView = mInflater.inflate(R.layout.item_example, parent, false); return new ExampleViewHolder(itemView); } @Override public void onBindViewHolder(ExampleViewHolder holder, int position) { final ExampleModel model = mModels.get(position); holder.bind(model); } @Override public int getItemCount() { return mModels.size(); } public void setModels(List<ExampleModel> models) { mModels = new ArrayList<>(models); }}Basically this is all you need to make this work. You can filter items in onQueryTextChange() and use the setModels() method to change the models in the Adapter and then call notifyDatasetChanged() on the Adapter to update the RecyclerView. Or at least that would have been the way to do this with a ListView. But the RecyclerView brings a whole new element to the table which we can take advantage off: out-of-the-box support for item animations!3) Getting the Adapter ready for item animationsThe first thing we have to do is define three helper methods which enable us to add, remove or move items around in the Adapter. Those methods will automatically call the required notify... method to trigger the item animation that goes along with it. public ExampleModel removeItem(int position) { final ExampleModel model = mModels.remove(position); notifyItemRemoved(position); return model;}public void addItem(int position, ExampleModel model) { mModels.add(position, model); notifyItemInserted(position);}public void moveItem(int fromPosition, int toPosition) { final ExampleModel model = mModels.remove(fromPosition); mModels.add(toPosition, model); notifyItemMoved(fromPosition, toPosition);}Again nothing special. We just modify the internal list of objects in the Adapter by either removing, adding or moving objects and once we are done call a notify... method.Now we are going to implement a method which will animate between the List of objects currently displayed in the Adapter to the filtered List we are going to supply to the method. public void animateTo(List<ExampleModel> models) { applyAndAnimateRemovals(models); applyAndAnimateAdditions(models); applyAndAnimateMovedItems(models);}The three methods contained in animateTo() do all the work here, but the order is important! The most difficult part about animating multiple items like this is keeping track of indexes. What I mean by that is that if for example you add an item then all items below the item you added are moved down. Equally if you remove an item all items below it are moved up. This is a big problem because all notify...() methods which trigger the item animations require the index of an item. If you are not careful and add or remove items and then try to call the notify... methods you are going to end up with weird glitchy animations or even an ArrayIndexOutOfBoundsException.We do two things to greatly simplify this problem for us:We have our 3 three helper methods from above which we can use to add, remove or move items and they automatically call the correct notify... method. So we don't have to worry about the animations anymore. We can purely concentrate on modifying the internal List of the Adapter.We define a specific order of operations when modifying the internal List of the Adapter.As I mentioned above we supply the filtered List to the Adapter by passing it into the animateTo() method. The first step is to remove all items which do not exist in the filtered List anymore. The next step is to add all items which did not exist in the original List but do in the filtered List. The final step is to move all items which exist in both Lists. This is exactly what the three methods in animateTo() do.Now we are going to look at the implementation of those three methods.Lets start in order with applyAndAnimateRemovals():private void applyAndAnimateRemovals(List<ExampleModel> newModels) { for (int i = mModels.size() - 1; i >= 0; i--) { final ExampleModel model = mModels.get(i); if (!newModels.contains(model)) { removeItem(i); } }}As you can see this method iterates through the internal List of the Adapter backwards and checks if each item is contained in the new filtered List. If it is not it calls removeItem(). The reason we iterate backwards is to avoid having to keep track of an offset. If you remove an item all items below it move up. If you iterate through to the List from the bottom up then only items which you have already iterated over are moved.Now lets look at applyAndAnimateAdditions():private void applyAndAnimateAdditions(List<ExampleModel> newModels) { for (int i = 0, count = newModels.size(); i < count; i++) { final ExampleModel model = newModels.get(i); if (!mModels.contains(model)) { addItem(i, model); } }}It basically does the same thing as applyAndAnimateRemovals() but instead of iterating through the internal List of the Adapter it iterates through the filtered List and checks if the item exists in the internal List. If it does not it calls addItem().And finally lets look at applyAndAnimateMovedItems():private void applyAndAnimateMovedItems(List<ExampleModel> newModels) { for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) { final ExampleModel model = newModels.get(toPosition); final int fromPosition = mModels.indexOf(model); if (fromPosition >= 0 && fromPosition != toPosition) { moveItem(fromPosition, toPosition); } }}This method implements a more complicated logic than the previous two. It is essentially a combination of applyAndAnimateRemovals() and applyAndAnimateAdditions() but with a twist. You have to realize that at this point applyAndAnimateRemovals() and applyAndAnimateAdditions() have already been called. So we have removed all the items that need to be removed and we added all new items which need to be added. So the internal List of the Adapter and the filtered List contain the exactly same items, but they may be in a different order. What applyAndAnimateMovedItems() now does is it iterates through the filtered List backwards and looks up the index of each item in the internal List. If it detects a difference in the index it calls moveItem() to bring the internal List of the Adapter in line with the filtered List.And with that our Adapter is complete. It can now display items and automatically triggers appropriate animations as we filter the List of objects. The only thing missing now is to connect the SearchView to the RecyclerView!One thing I should still mention is that while this is a very simple approach to animating the items it is by far not the most efficient one. But for most use cases it should be more than enough and once you understand the gist of it you can implement it very quickly.4) Implementing the filter logicOne thing in your question which caught my eye is that you maintain the list of items you want to display directly in the Adapter. While the Adapter of course has to have a List of items internally you should not completely maintain the List in there. The Adapter - as the name implies - should just turn Objects into Views. You give it a List of Objects and the RecyclerView gets a View representing each Object. What the Adapter is definitely not responsible for is creating those Objects.To implement the filter logic we first have to define a List of all possible Objects. As I mentioned above we will do that not inside the Adapter, but outside in the Fragment or Activity which contains the RecyclerView. The basic implementation (in my case for a Fragment) looks like this:private RecyclerView mRecyclerView;private ExampleAdapter mAdapter;private List<ExampleModel> mModels;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_main, container, false); mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); return view;}@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setHasOptionsMenu(true); mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mModels = new ArrayList<>(); for (String movie : MOVIES) { mModels.add(new ExampleModel(movie)); } mAdapter = new ExampleAdapter(getActivity(), mModels); mRecyclerView.setAdapter(mAdapter);}Again nothing special. We setup the RecyclerView and instantiate the Adapter. We create a List of items and store that in a field. This list will be our reference. It contains all possible items which we will filter with the SearchView. In the above example MOVIES is a String[] I defined which contains my test data.Now we can go back to onQueryTextChange() which we defined earlier and start implementing the filter logic:@Overridepublic boolean onQueryTextChange(String query) { final List<ExampleModel> filteredModelList = filter(mModels, query); mAdapter.animateTo(filteredModelList); mRecyclerView.scrollToPosition(0); return true;}This is again pretty straight forward. We call the method filter() and pass in our reference List of objects and the query string. We then call animateTo() on the Adapter and pass in the filtered List returned by filter(). We also have to call scrollToPosition(0) on the RecyclerView to ensure that the user can always see all items when searching for something. Otherwise the RecyclerView might stay in a scrolled down position while filtering and subsequently hide a few items. Scrolling to the top ensures a better user experience while searching.The only thing left to do now is to implement filter() itself:private List<ExampleModel> filter(List<ExampleModel> models, String query) { query = query.toLowerCase(); final List<ExampleModel> filteredModelList = new ArrayList<>(); for (ExampleModel model : models) { final String text = model.getText().toLowerCase(); if (text.contains(query)) { filteredModelList.add(model); } } return filteredModelList;}The first thing we do here is call toLowerCase() on the query string. We don't want our search function to be case sensitive and by calling toLowerCase() on all strings we compare we can ensure that we return the same results regardless of case.filter() basically just iterates through all the models in the List we passed into it and checks if the query string is contained in the text of the model. If it is then the model is added to the filtered List.And that is pretty much it! If you run your app now you should be able to filter the data in the RecyclerView with the SearchView and this whole thing runs on Froyo (Android 2.2, API level 7) and above! And starting with Honeycomb (Android 3.0, API level 11) all changes to the dataset displayed in the RecyclerView will be animated automatically!I realize that this is a very detailed description which probably makes this whole thing seem more complicated than it really is. I suggest you look at the working example in the GitHub Repository I linked to above if you are having trouble understanding a specific aspect of this whole thing. 这篇关于如何与一个搜索查看过滤RecyclerView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 09-12 22:34