当前位置: 首页 > 知识库问答 >
问题:

如何用SearchView筛选RecyclerView

江仲渊
2023-03-14

我正在尝试从支持库中实现SearchView。我希望用户使用SearchViewRecyclerView中筛选电影的列表

到目前为止,我已经学习了一些教程,并且已经将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);
    }
}

这是我的适配器:

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);
        }
    }
}

共有2个答案

仇正平
2023-03-14

您只需在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();
}

ItemScope在适配器的构造函数中初始化,类似于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;
    }
});

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

黄淇
2023-03-14

由于从您的问题中并不清楚您到底遇到了什么问题,所以我编写了这个关于如何实现该特性的快速演练;如果你还有问题,尽管问。

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

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

如果你想先玩一下演示应用程序,你可以从play Store安装它:

不管怎样,让我们开始吧。

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>

在您的片段activity中,您必须像往常一样膨胀此菜单xml,然后您可以查找包含SearchViewMenuItem并实现OnQueryTextListener,我们将使用该OnQueryTextListener来侦听对输入SearchView中的文本的更改:

@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) searchItem.getActionView();
    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()中实现筛选逻辑。

首先,这是我将在本例中使用的模型类:

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中定义的那样。

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

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

sortedlist是一个非常棒的工具,它是recyclerview库的一部分。它负责通知适配器有关数据集的更改,这是一种非常有效的方式。它要求您做的唯一事情是指定元素的顺序。您需要通过实现compare()方法来做到这一点,该方法将sortedlist中的两个元素进行比较,就像比较器一样。但它不是对列表进行排序,而是对回收视图中的项目进行排序!

SortedList通过必须实现的Callback类与适配器交互:

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等,您必须调用适配器的等效notify方法。底部的三个方法comparearecontentsthesameareitemsthesame您必须根据您要显示的对象类型以及这些对象应该以什么顺序出现在屏幕上来实现。

下面就让我们一一来过一下这些方法:

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

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

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实现适配器,它应该如下所示:

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();
    }
}

用于排序项的比较器是通过构造函数传入的,因此即使项应该以不同的顺序显示,我们也可以使用相同的适配器

现在我们快完成了!但我们首先需要一种向适配器添加或删除项的方法。为此,我们可以向适配器添加方法,这些方法允许我们向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();
}

我们不需要在这里调用任何notify方法,因为SortedList已经通过SortedList.Callback执行了此操作!除此之外,这些方法的实现非常简单,只有一个例外:remove方法,它删除模型的list。因为sortedlist只有一个remove方法可以删除单个对象,所以我们需要在列表上循环并逐个删除模型。在开始时调用beginbatchedupdates()将对sortedlist进行的所有更改批处理在一起,从而提高了性能。当我们调用EndBatchedUpDates()时,RecyclerView会被一次通知所有更改。

此外,您必须理解的是,如果您向sortedlist添加对象,并且该对象已经在sortedlist中,则不会再次添加该对象。相反,SortedList使用AreContentsTheSame()方法来确定对象是否已更改,以及是否已更改RecycerView中的项将被更新。

无论如何,我通常更喜欢一种方法,它允许我一次替换RecyclerView中的所有项。删除列表中没有的所有内容,并添加排序列表中缺少的所有项目:

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中已经存在但已经更改的所有项。

这样,适配器就完成了。整件事看起来应该是这样的:

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();
    }
}

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

要实现筛选逻辑,我们首先必须定义所有可能模型的列表。对于本例,我从电影数组中创建一个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);
}

这里没有什么特别的内容,我们只是实例化适配器并将其设置为RecyclerView。之后,我们根据movies数组中的电影名称创建模型的列表。然后我们将所有模型添加到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以及查询字符串。然后调用适配器上的ReplaceAll()并传入filter()返回的经过筛选的列表。我们还必须在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中的所有模型,并检查查询字符串是否包含在模型的文本中。如果是,则将模型添加到筛选的列表中。

就这样了!上面的代码将运行在API级别7和以上,从API级别11开始,你可以免费获得项目动画!

我意识到这是一个非常详细的描述,它可能使整个事情看起来比实际情况复杂,但有一种方法可以使整个问题泛化,并使基于sortedlist实现适配器简单得多。

在本节中,我将不做太多的详细介绍--部分原因是我遇到了关于堆栈溢出的字符限制,但也是因为上面已经解释过了--而是总结一下更改:我们可以实现一个基本的adapter类,它已经处理了sortedlist以及将模型绑定到viewholder实例,并提供了一种基于sortedlist实现适配器的方便方法。为此,我们必须做两件事:

  • 我们需要创建一个viewmodel接口,所有模型类都必须实现这个接口
  • 我们需要创建一个viewholder子类,它定义了适配器可以用来自动绑定模型的bind()方法。

这允许我们只关注应该通过实现模型和相应的viewholder实现在recyclerView中显示的内容。使用这个基类,我们不必担心适配器及其SortedList的复杂细节。

由于StackOverflow上答案的字符限制,我不能在这里详细介绍实现这个基类的每个步骤,甚至不能在这里添加完整的源代码,但是您可以在这个GitHub要点中找到这个基类的完整源代码--我将其称为SortedListAdapter

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

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

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

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

>

  • 更改查看器以便它扩展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。但如果您使用的是不同类型的模型,则将type参数设置为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(),则不会应用您的任何更改!

  •  类似资料:
    • 关于searchview,我有一个简单的问题--我在我的菜单中创建了searchview项,我有功能gridview,但我不知道如何在onCreateOptionMenu中完成我的代码--它不会识别我的GridAdapter。以下代码 gridAdapter是我的gridview的适配器,但它显示为红色,这是我不明白的。顺便说一句:即使在OnCreateOptionsMenu中添加了这两行,应用程

    • 我需要在我的android应用程序中实现搜索功能,该应用程序使用工具栏、SlidingTableOut和保存片段的ViewPager。在每个片段中都有一个带有项目列表的RecyclerView。 recycle view数据是在单独的类(DataAccess.java)中静态定义的,只需调用(不传递新数据)即可更新这些列表并刷新recycle view 有什么简单的方法可以在不改变数据的情况下,在

    • FundingRank和SourceBucket是枚举,但本质上是int 因此,我试图对treemap >进行排序,排序顺序定义为MinFunding rank和max scorrebucket(阈值为5,如果bucket有5个孩子,这意味着如果有其他更小的bucket有更多的孩子,得分更高的bucket获胜,或者如果有更大的fundinggrank有更高的bucket大小和更多的孩子,仍然是更低

    • 如何在枚举的帮助下筛选我的“StorageManager”在其名为“items”的LinkedList中的项目?受此筛选器影响的项目需要复制到新的LinkedList中。我用buyNewItem()将项目添加到存储管理器列表中,但第二项任务需要帮助。 如果我忘记添加一些重要信息,请让我知道。 编辑:仅允许重新导入的是 导入java.util.数组; 导入java.util.LinkedList;

    • Kotlin 如何使用 SearchBar + SearchView? https://m3.material.io/components/search/overview 显示 Jetpack Compose 的还在计划中,但是有 MDC 的 能否在 Kotlin 里使用 MDC 提供的 SearchBar + SearchView? 如果可以,应该怎么使用?

    • 问题内容: 回调函数仅传递数组的值,而不传递键。 如果我有: 删除数组中所有不在其中的键的最佳方法是什么? 所需的输出: 问题答案: PHP 5.6引入了第三个参数,,你可以设置为通过键,而不是值进行筛选: 显然,这不如优雅,但是它确实提供了额外的灵活性,可以对键执行任意测试,例如可以包含正则表达式模式而不是纯字符串。 您还可以将值和键都传递给过滤器函数。这是一个基于第一个示例的示例,但请注意,我