如何使用SearchView过滤RecyclerView

我正试图从支持库实现SearchView 。 我希望用户能够使用SearchViewRecyclerView过滤电影List

到目前为止,我已经学习了几个教程,并且已经将SearchView添加到了ActionBar ,但我不确定从哪里开始。 我已经看过几个例子,但是当你开始输入时,他们都没有显示结果。

这是我的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);
    }
}

这是我的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);
        }
    }
}

介绍

既然你不清楚自己有什么问题,就写下这篇关于如何实现这个功能的快速演练,如果你仍然有问题可以随意问。

我在这个GitHub存储库中有一个我正在讨论的一切的工作示例。
如果您想了解更多关于示例项目的信息,请访问项目主页

无论如何,结果应该如下所示:

演示图像

如果您首先想要使用演示应用程序,则可以从Play商店安装它:

无论如何,让我们开始吧。


设置SearchView

在文件夹res/menu创建一个名为main_menu.xml的新文件。 在它中添加一个项目并将actionViewClass设置为android.support.v7.widget.SearchView 。 由于您正在使用支持库,因此必须使用支持库的名称空间来设置actionViewClass属性。 你的xml文件应该是这样的:

<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>

在你的FragmentActivity你必须像往常一样对这个菜单xml进行膨胀,然后你可以查找包含SearchViewMenuItem ,并实现我们要用来侦听输入到SearchView的文本的更改的OnQueryTextListener

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

    final MenuItem searchItem = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    searchView.setOnQueryTextListener(this);

    return true;
}

@Override
public boolean onQueryTextChange(String query) {
    // Here is where we are going to implement the filter logic
    return false;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

现在, SearchView已经可以使用了。 稍后我们将在onQueryTextChange()实现过滤器逻辑,一旦我们完成了Adapter实现。


设置Adapter

首先,这是我将用于此示例的模型类:

public class ExampleModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }
}

这只是您在RecyclerView显示文本的基本模型。 这是我将用于显示文本的布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="model"
            type="com.github.wrdlbrnft.searchablerecyclerviewdemo.ui.models.ExampleModel"/>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:clickable="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{model.text}"/>

    </FrameLayout>

</layout>

正如你所看到的,我使用数据绑定。 如果你从未使用过数据绑定,请不要气馁! 它非常简单而强大,但是我无法解释它在这个答案的范围内是如何工作的。

这是ExampleModel类的ViewHolder

public class ExampleViewHolder extends RecyclerView.ViewHolder {

    private final ItemExampleBinding mBinding;

    public ExampleViewHolder(ItemExampleBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
    }

    public void bind(ExampleModel item) {
        mBinding.setModel(item);
    }
}

再没什么特别的。 它只是使用数据绑定将模型类绑定到这个布局,就像我们在上面的布局xml中定义的那样。

现在我们终于可以来到真正有趣的部分:编写适配器。 我将跳过Adapter的基本实现,而不是专注于与此答案相关的部分。

但首先我们必须谈论一件事: SortedList类。


排序列表

SortedList是一个完全了不起的工具,它是RecyclerView库的一部分。 它负责通知Adapter有关数据集的更改,这样做是非常有效的方法。 它需要你做的唯一事情就是指定元素的顺序。 您需要通过实现compare()方法来比较SortedList两个元素,就像Comparator 。 但不是排序一个List ,而是用来排序RecyclerView的项目!

SortedList通过一个你必须实现的Callback类与Adapter进行交互:

private final SortedList.Callback<ExampleModel> mCallback = new SortedList.Callback<ExampleModel>() {

    @Override
    public void onInserted(int position, int count) {
         mAdapter.notifyItemRangeInserted(position, count);
    }

    @Override
    public void onRemoved(int position, int count) {
        mAdapter.notifyItemRangeRemoved(position, count);
    }

    @Override
    public void onMoved(int fromPosition, int toPosition) {
        mAdapter.notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onChanged(int position, int count) {
        mAdapter.notifyItemRangeChanged(position, count);
    }

    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return mComparator.compare(a, b);
    }

    @Override
    public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
        return oldItem.equals(newItem);
    }

    @Override
    public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
        return item1.getId() == item2.getId();
    }
}

onMovedonInserted等回调顶部的方法中,您必须调用Adapter的等效通知方法。 底部的三个方法compareareContentsTheSameareItemsTheSame你必须根据你想要显示什么样的对象和这些对象应该以什么顺序出现在屏幕上实现。

让我们一个接一个地看看这些方法:

@Override
public int compare(ExampleModel a, ExampleModel b) {
    return mComparator.compare(a, b);
}

这是我前面谈到的compare()方法。 在这个例子中,我只是将调用传递给Comparator两个模型的比较器。 如果您希望项目在屏幕上按字母顺序显示。 这个比较器可能看起来像这样:

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

现在让我们看看下一个方法:

@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
    return oldItem.equals(newItem);
}

此方法的目的是确定模型的内容是否已更改。 SortedList使用它来确定是否需要调用更改事件 - 换句话说,如果RecyclerView应该交叉淡入淡出旧版本和新版本。 如果你的模型类有一个正确的equals()hashCode()实现,你通常可以像上面那样实现它。 如果我们将一个equals()hashCode()实现添加到ExampleModel类中,它应该看起来像这样:

public class ExampleModel implements SortedListAdapter.ViewModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ExampleModel model = (ExampleModel) o;

        if (mId != model.mId) return false;
        return mText != null ? mText.equals(model.mText) : model.mText == null;

    }

    @Override
    public int hashCode() {
        int result = (int) (mId ^ (mId >>> 32));
        result = 31 * result + (mText != null ? mText.hashCode() : 0);
        return result;
    }
}

快速的一面注意:大多数IDE的Android Studio,IntelliJ和Eclipse都具有在按下按钮时为您生成equals()hashCode()实现的功能! 所以你不必自己实现它们。 在互联网上查找它在IDE中的工作原理!

现在我们来看看最后一个方法:

@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
    return item1.getId() == item2.getId();
}

SortedList使用这种方法来检查两个项目是否引用相同的东西。 用最简单的术语(没有解释SortedList是如何工作的),这用于确定List是否已经包含对象,以及是否需要播放添加,移动或更改动画。 如果你的模型有一个id,你通常会比较这个方法中的id。 如果他们没有,你需要找出其他方法来检查这一点,但是最终实现这个取决于你的特定应用程序。 通常,为所有模型提供一个id是最简单的选择 - 例如,如果您正在查询数据库中的数据,则可能是主键字段。

通过正确实现SortedList.Callback ,我们可以创建SortedList一个实例:

final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);

作为SortedList的构造函数中的第一个参数,您需要传递模型的类。 另一个参数就是我们上面定义的SortedList.Callback

现在让我们SortedList业务:如果我们使用SortedList实现Adapter ,它应该看起来像这样:

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1.getId() == item2.getId();
        }
    });

    private final LayoutInflater mInflater;
    private final Comparator<ExampleModel> mComparator;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

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

用于对项目进行排序的Comparator器通过构造函数传入,因此即使项目应按不同的顺序显示,我们也可以使用相同的Adapter

现在我们快完成了! 但我们首先需要一种方法来添加或删除项目到Adapter 。 为了这个目的,我们可以向Adapter添加方法,这些方法允许我们向SortedList添加和移除项目:

public void add(ExampleModel model) {
    mSortedList.add(model);
}

public void remove(ExampleModel model) {
    mSortedList.remove(model);
}

public void add(List<ExampleModel> models) {
    mSortedList.addAll(models);
}

public void remove(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (ExampleModel model : models) {
        mSortedList.remove(model);
    }
    mSortedList.endBatchedUpdates();
}

我们不需要在这里调用任何通知方法,因为SortedList已经通过SortedList.Callback执行此操作! 除了这些方法的实现非常简单,只有一个例外:删除模型List的remove方法。 由于SortedList只有一个可以移除单个对象的移除方法,因此我们需要遍历列表并逐个移除模型。 在开始处调用beginBatchedUpdates()会将我们要对SortedList进行的所有更改批处理并提高性能。 当我们调用endBatchedUpdates()RecyclerView会立即通知所有更改。

此外,你必须明白的是,如果你添加一个对象到SortedList并且它已经在SortedList ,它将不会再被添加。 相反, SortedList使用areContentsTheSame()方法来确定对象是否已更改 - 并且RecyclerView的项目是否会更新。

无论如何,我通常喜欢的是一种方法,它允许我一次替换RecyclerView中的所有项目。 删除不在List中的所有内容,并添加SortedList中缺少的所有项目:

public void replaceAll(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (int i = mSortedList.size() - 1; i >= 0; i--) {
        final ExampleModel model = mSortedList.get(i);
        if (!models.contains(model)) {
            mSortedList.remove(model);
        }
    }
    mSortedList.addAll(models);
    mSortedList.endBatchedUpdates();
}

该方法再次将所有更新批量化以提高性能。 第一个循环是相反的,因为在开始时删除一个项目会混淆后面出现的所有项目的索引,这可能会导致某些情况下出现数据不一致等问题。 之后,我们只需使用addAll()List添加到SortedList即可添加尚未在SortedList中的所有项目,并且 - 就像我上面所述 - 更新已在SortedList但已更改的所有项目。

并且由于Adapter是完整的。 整件事应该看起来像这样:

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1 == item2;
        }
    });

    private final Comparator<ExampleModel> mComparator;
    private final LayoutInflater mInflater;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(mInflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

    public void add(ExampleModel model) {
        mSortedList.add(model);
    }

    public void remove(ExampleModel model) {
        mSortedList.remove(model);
    }

    public void add(List<ExampleModel> models) {
        mSortedList.addAll(models);
    }

    public void remove(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (ExampleModel model : models) {
            mSortedList.remove(model);
        }
        mSortedList.endBatchedUpdates();
    }

    public void replaceAll(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (int i = mSortedList.size() - 1; i >= 0; i--) {
            final ExampleModel model = mSortedList.get(i);
            if (!models.contains(model)) {
                mSortedList.remove(model);
            }
        }
        mSortedList.addAll(models);
        mSortedList.endBatchedUpdates();
    }

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

现在唯一缺少的是实现过​​滤!


实现过滤器逻辑

为了实现过滤器逻辑,我们首先必须定义所有可能模型的List 。 在这个例子中,我从一组电影中创建了一个ExampleModel实例List

private static final String[] MOVIES = new String[]{
        ...
};

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

private ExampleAdapter mAdapter;
private List<ExampleModel> mModels;
private RecyclerView mRecyclerView;

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    mAdapter = new ExampleAdapter(this, ALPHABETICAL_COMPARATOR);

    mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
    mBinding.recyclerView.setAdapter(mAdapter);

    mModels = new ArrayList<>();
    for (String movie : MOVIES) {
        mModels.add(new ExampleModel(movie));
    }
    mAdapter.add(mModels);
}

这里没有什么特别的,我们只是实例化Adapter并将其设置为RecyclerView 。 之后,我们根据MOVIES数组中的电影名称创建一个模型List 。 然后我们将所有模型添加到SortedList

现在我们可以回到我们之前定义的onQueryTextChange()并开始实现过滤器逻辑:

@Override
public boolean onQueryTextChange(String query) {
    final List<ExampleModel> filteredModelList = filter(mModels, query);
    mAdapter.replaceAll(filteredModelList);
    mBinding.recyclerView.scrollToPosition(0);
    return true;
}

这再次非常简单。 我们调用方法filter()并传入ExampleModelList以及查询字符串。 然后,我们调用Adapter上的replaceAll()并传递filter()返回的过滤List 。 我们还必须在RecyclerView上调用scrollToPosition(0) ,以确保用户在搜索某些内容时始终可以看到所有项目。 否则, RecyclerView可能会停留在向下滚动的位置,同时进行过滤并随后隐藏一些项目。 滚动到顶部可确保在搜索时获得更好的用户体验。

现在要做的唯一事情就是实现filter()本身:

private static List<ExampleModel> filter(List<ExampleModel> models, String query) {
    final String lowerCaseQuery = query.toLowerCase();

    final List<ExampleModel> filteredModelList = new ArrayList<>();
    for (ExampleModel model : models) {
        final String text = model.getText().toLowerCase();
        if (text.contains(lowerCaseQuery)) {
            filteredModelList.add(model);
        }
    }
    return filteredModelList;
}

我们在这里做的第一件事是在查询字符串上调用toLowerCase() 。 我们不希望我们的搜索功能区分大小写,并且通过对所有我们比较的字符串调用toLowerCase() ,我们可以确保无论大小写都返回相同的结果。 然后,它只是遍历我们传入的List中的所有模型,并检查查询字符串是否包含在模型的文本中。 如果是,则将该模型添加到过滤的List

就是这样! 上述代码将运行在API级别7及以上,并从API级别11开始,您可以免费获得项目动画!

我意识到这是一个非常详细的描述,可能会让整个事情看起来比实际更复杂,但是我们可以将这个问题概括化,并使基于SortedListAdapter实现更简单。


推广问题并简化适配器

在这一节中,我不会详细讨论 - 部分原因是我针对堆栈溢出的答案的字符限制,但也因为它大部分已经在上面解释过 - 但总结了一些变化:我们可以实现一个基础Adapter已经处理SortedList类以及绑定到ViewHolder实例的模型,并提供了一种基于SortedList实现Adapter的简便方法。 为此,我们必须做两件事:

  • 我们需要创建一个所有模型类必须实现的ViewModel接口
  • 我们需要创建一个ViewHolder子类,该子类定义了Adapter可以用来自动绑定模型的bind()方法。
  • 这允许我们只关注应该在RecyclerView显示的内容,只需实现模型并在ViewHolder实现相应的ViewHolder实现。 使用这个基类,我们不必担心Adapter及其SortedList的复杂细节。

    SortedListAdapter

    由于StackOverflow的字符数限制,我无法通过实现这个基类的每一步,甚至在这里添加完整的源代码,但是你可以找到这个基类的完整源代码 - 我称它为SortedListAdapter - in这个GitHub Gist

    为了简化你的生活,我在jCenter上发布了一个包含SortedListAdapter的库! 如果你想使用它,那么你所需要做的就是将这个依赖项添加到你的应用程序的build.gradle文件中:

    compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'
    

    你可以在图书馆的主页上找到关于这个图书馆的更多信息。

    使用SortedListAdapter

    要使用SortedListAdapter我们必须做两个更改:

  • 更改ViewHolder ,使其扩展SortedListAdapter.ViewHolder 。 类型参数应该是应该绑定到这个ViewHolder的模型 - 在这个例子中是ExampleModel 。 您必须在performBind()而不是bind()中将数据绑定到模型。

    public class ExampleViewHolder extends SortedListAdapter.ViewHolder<ExampleModel> {
    
        private final ItemExampleBinding mBinding;
    
        public ExampleViewHolder(ItemExampleBinding binding) {
            super(binding.getRoot());
            mBinding = binding;
        }
    
        @Override
        protected void performBind(ExampleModel item) {
            mBinding.setModel(item);
        }
    }
    
  • 确保所有模型都实现了ViewModel接口:

    public class ExampleModel implements SortedListAdapter.ViewModel {
        ...
    }
    
  • 之后,我们只需更新ExampleAdapter以扩展SortedListAdapter并删除我们不再需要的所有内容。 类型参数应该是您正在使用的模型类型 - 在本例中为ExampleModel 。 但是,如果您使用不同类型的模型,请将类型参数设置为ViewModel

    public class ExampleAdapter extends SortedListAdapter<ExampleModel> {
    
        public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
            super(context, ExampleModel.class, comparator);
        }
    
        @Override
        protected ViewHolder<? extends ExampleModel> onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) {
            final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
            return new ExampleViewHolder(binding);
        }
    
        @Override
        protected boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1.getId() == item2.getId();
        }
    
        @Override
        protected boolean areItemContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }
    }
    

    之后,我们完成了! 但最后要提的一件事是: SortedListAdapter不具有我们的原始ExampleAdapter所具有的add()remove()replaceAll()方法。 它使用一个单独的Editor对象来修改可以通过edit()方法访问的列表中的项目。 所以,如果你想删除或添加项目,你必须调用edit()然后添加和删除这个Editor实例上的项目,一旦你完成,调用commit()将更改应用到SortedList

    mAdapter.edit()
            .remove(modelToRemove)
            .add(listOfModelsToAdd)
            .commit();
    

    您以这种方式进行的所有更改都会一起批处理以提高性能。 我们在上面章节中实现的replaceAll()方法也出现在这个Editor对象上:

    mAdapter.edit()
            .replaceAll(mModels)
            .commit();
    

    如果您忘记调用commit()则不会应用您的更改!


    您需要做的就是在RecyclerView.Adapter添加filter方法:

    public void filter(String text) {
        items.clear();
        if(text.isEmpty()){
            items.addAll(itemsCopy);
        } else{
            text = text.toLowerCase();
            for(PhoneBookItem item: itemsCopy){
                if(item.name.toLowerCase().contains(text) || item.phone.toLowerCase().contains(text)){
                    items.add(item);
                }
            }
        }
        notifyDataSetChanged();
    }
    

    itemsCopy在适配器的构造函数中初始化,如itemsCopy.addAll(items)

    如果你这样做,只需从OnQueryTextListener调用filter

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            adapter.filter(query);
            return true;
        }
    
        @Override
        public boolean onQueryTextChange(String newText) {
            adapter.filter(newText);
            return true;
        }
    });
    

    这是通过姓名和电话号码过滤我的电话簿的一个例子。


    继@Shruthi Kamoji之后,我们可以使用可过滤的,它的意思是:

    public abstract class GenericRecycleAdapter<E> extends RecyclerView.Adapter implements Filterable
    {
      protected List<E> list;
      protected List<E> originalList;
      protected Context context;
    
      public GenericRecycleAdapter(Context context,
                                 List<E> list)
      {
        this.originalList = list;
        this.list = list;
        this.context = context;
      }
    
    ...
    
    @Override
    public Filter getFilter() {
        return new Filter() {
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                list = (List<E>) results.values;
                GenericRecycleAdapter.this.notifyDataSetChanged();
            }
    
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                List<E> filteredResults = null;
                if (constraint.length() == 0) {
                    filteredResults = originalList;
                } else {
                    filteredResults = getFilteredResults(constraint.toString().toLowerCase());
                }
    
                FilterResults results = new FilterResults();
                results.values = filteredResults;
    
                return results;
            }
        };
    }
    
      protected List<E> getFilteredResults(String constraint) {
         List<E> results = new ArrayList<>();
    
          for (E item : originalList) {
              if (item.getName().toLowerCase().contains(constraint)) {
                  results.add(item);
              }
          }
          return results;
     }
    
    
    } 
    

    这里的E是一个泛型类型,你可以使用你的类来扩展它:

    public class customerAdapter extends GenericRecycleAdapter<CustomerModel>
    

    或者把E改为你想要的类型(例如<CustomerModel>

    然后从searchView(你可以放在menu.xml中的小部件):

     searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String text) {
            return false;
        }
    
        @Override
        public boolean onQueryTextChange(String text) {
            yourAdapter.getFilter().filter(text);
            return true;
        }
    });
    
    链接地址: http://www.djcxy.com/p/92947.html

    上一篇: How to filter a RecyclerView with a SearchView

    下一篇: Reset the SearchView on fragment change in ViewPager with ActionBar tabs