SearchViewでRecyclerViewをフィルタリングする方法 質問する

SearchViewでRecyclerViewをフィルタリングする方法 質問する

サポート ライブラリから を実装しようとしていますSearchView。 ユーザーが を使用して内の映画の をSearchViewフィルターできるようにしたいと考えています。ListRecyclerView

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

ベストアンサー1

導入

質問から、具体的に何に問題があるのか​​がよく分からないので、この機能を実装する方法について簡単なチュートリアルを書きました。それでも質問がある場合は、遠慮なく質問してください。

私がここで話しているすべてのことの実例をここに載せていますGitHub リポジトリ

いずれにしても、結果は次のようになります。

デモ画像

まずデモ アプリを試してみたい場合は、Play ストアからインストールできます。

Google Playで入手する

とにかく始めましょう。


設定SearchView

フォルダーにres/menuという新しいファイルを作成しますmain_menu.xml。その中に項目を追加し、 を に設定しますactionViewClassandroid.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>

または では、Fragment通常どおりこのメニュー xml を膨張させる必要があります。次に、を含むActivityを探し、に入力されたテキストの変更をリッスンするために使用するを実装します。MenuItemSearchViewOnQueryTextListenerSearchView

@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()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>

ご覧のとおり、私はデータ バインディングを使用しています。これまでデータ バインディングを使用したことがなくても、落胆しないでください。これは非常にシンプルで強力ですが、この回答の範囲内でどのように機能するかを説明することはできません。

これはクラスViewHolderのものですExampleModel:

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通知し、非常に効率的な方法で通知します。必要なのは、要素の順序を指定することだけです。そのためには、 のように内の 2 つの要素を比較するメソッドを実装する必要があります。ただし、 をソートする代わりに、内の項目をソートするために使用されます。Adaptercompare()SortedListComparatorListRecyclerView

は、実装する必要があるクラスを通じてSortedListと対話します。AdapterCallback

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

onMoved、などのコールバックの上部にあるメソッドでは、onInsertedの同等の通知メソッドを呼び出す必要がありますAdapter。 、および の下部にある 3 つのメソッドはcompare、表示するオブジェクトの種類と、これらのオブジェクトを画面に表示する順序に応じて実装する必要があります。areContentsTheSameareItemsTheSame

これらの方法を一つずつ見ていきましょう。

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

これはcompare()先ほど説明した方法です。この例では、Comparator2 つのモデルを比較する呼び出しを渡すだけです。画面に項目をアルファベット順に表示したい場合、この比較器は次のようになります。

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()andhashCode()実装がある場合は、通常は上記のように実装できます。equals()andhashCode()実装をクラスに追加すると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;
    }
}

ちょっとした補足: Android Studio、IntelliJ、Eclipse などのほとんどの IDE には、ボタンを押すだけで生成しequals()hashCode()実装する機能があります。そのため、自分で実装する必要はありません。インターネットで、IDE でどのように動作するかを調べてください。

それでは最後の方法を見てみましょう。

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

SortedList、このメソッドを使用して、2 つの項目が同じものを参照しているかどうかを確認します。簡単に言えば ( のSortedList動作の説明は省略します)、これは、オブジェクトがすでに に含まれているかどうかList、および追加、移動、または変更アニメーションを再生する必要があるかどうかを判断するために使用されます。モデルに ID がある場合は、通常、このメソッドで ID のみを比較します。モデルに ID がない場合は、これを確認するための別の方法を見つける必要がありますが、これを最終的に実装する方法は、特定のアプリケーションによって異なります。通常、すべてのモデルに ID を付与するのが最も簡単な方法です。たとえば、データベースからデータを照会する場合は、その ID を主キー フィールドとすることができます。

を正しく実装すれば、SortedList.Callbackのインスタンスを作成できますSortedList

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

As the first parameter in the constructor of the SortedList you need to pass the class of your models. The other parameter is just the SortedList.Callback we defined above.

Now let's get down to business: If we implement the Adapter with a SortedList it should look something like this:

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

The Comparator used to sort the item is passed in through the constructor so we can use the same Adapter even if the items are supposed to be displayed in a different order.

Now we are almost done! But we first need a way to add or remove items to the Adapter. For this purpose we can add methods to the Adapter which allow us to add and remove items to the 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();
}

We don't need to call any notify methods here because the SortedList already does this for through the SortedList.Callback! Aside from that the implementation of these methods is pretty straight forward with one exception: the remove method which removes a List of models. Since the SortedList has only one remove method which can remove a single object we need to loop over the list and remove the models one by one. Calling beginBatchedUpdates() at the beginning batches all the changes we are going to make to the SortedList together and improves performance. When we call endBatchedUpdates() the RecyclerView is notified about all the changes at once.

Additionally what you have to understand is that if you add an object to the SortedList and it is already in the SortedList it won't be added again. Instead the SortedList uses the areContentsTheSame() method to figure out if the object has changed - and if it has the item in the RecyclerView will be updated.

Anyway, what I usually prefer is one method which allows me to replace all items in the RecyclerView at once. Remove everything which is not in the List and add all items which are missing from the 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();
}

This method again batches all updates together to increase performance. The first loop is in reverse since removing an item at the start would mess up the indexes of all items that come up after it and this can lead in some instances to problems like data inconsistencies. After that we just add the List to the SortedList using addAll() to add all items which are not already in the SortedList and - just like I described above - update all items that are already in the SortedList but have changed.

And with that the Adapter is complete. The whole thing should look something like this:

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

The only thing missing now is to implement the filtering!


Implementing the filter logic

To implement the filter logic we first have to define a List of all possible models. For this example I create a List of ExampleModel instances from an array of movies:

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

Nothing special going on here, we just instantiate the Adapter and set it to the RecyclerView. After that we create a List of models from the movie names in the MOVIES array. Then we add all the models to the SortedList.

Now we can go back to onQueryTextChange() which we defined earlier and start implementing the filter logic:

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

This is again pretty straight forward. We call the method filter() and pass in the List of ExampleModels as well as the query string. We then call replaceAll() 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 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;
}

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. It then 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's it! The above code will run on API level 7 and above and starting with API level 11 you get item animations for free!

I realize that this is a very detailed description which probably makes this whole thing seem more complicated than it really is, but there is a way we can generalize this whole problem and make implementing an Adapter based on a SortedList much simpler.


Generalizing the problem and simplifying the Adapter

In this section I am not going to go into much detail - partly because I am running up against the character limit for answers on Stack Overflow but also because most of it already explained above - but to summarize the changes: We can implemented a base Adapter class which already takes care of dealing with the SortedList as well as binding models to ViewHolder instances and provides a convenient way to implement an Adapter based on a SortedList. For that we have to do two things:

  • We need to create a ViewModel interface which all model classes have to implement
  • We need to create a ViewHolder subclass which defines a bind() method the Adapter can use to bind models automatically.

This allows us to just focus on the content which is supposed to be displayed in the RecyclerView by just implementing the models and there corresponding ViewHolder implementations. Using this base class we don't have to worry about the intricate details of the Adapter and its SortedList.

ソートリストアダプタ

StackOverflowの回答の文字数制限のため、この基本クラスの実装の各ステップを説明したり、完全なソースコードをここに追加したりすることはできませんが、この基本クラス(私がそう呼んでいます)の完全なソースコードはSortedListAdapterここにあります。GitHub の Gist

あなたの生活をシンプルにするために、 を含むライブラリを jCenter に公開しましたSortedListAdapter。これを使用したい場合は、アプリの build.gradle ファイルに次の依存関係を追加するだけです。

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

このライブラリに関する詳細情報は図書館のホームページ

SortedListAdapter の使用

を使用するには、SortedListAdapter次の 2 つの変更を行う必要があります。

  • 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、不要になったものをすべて削除するだけです。 type パラメータは、作業しているモデルのタイプ (この場合は ) である必要があります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);
    }
}

これで完了です。ただし、最後にもう 1 つ言及しておくことがあります。 には、元の にあったのとSortedListAdapter同じadd()remove()、またはメソッドがありません。 は、 メソッドを通じてアクセスできるリスト内の項目を変更するために別のオブジェクトを使用します。したがって、項目を削除または追加したい場合は、 を呼び出して、このインスタンスで項目を追加および削除し、完了したら を呼び出して に変更を適用する必要があります。replaceAll()ExampleAdapterEditoredit()edit()Editorcommit()SortedList

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

この方法で行った変更はすべてバッチ処理され、パフォーマンスが向上します。replaceAll()上の章で実装したメソッドもこのEditorオブジェクトに存在します。

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

電話を忘れるとcommit()、変更は一切適用されません。

おすすめ記事