1. ホーム
  2. android

[解決済み] RecyclerViewをSearchViewでフィルタリングする方法

2022-03-15 19:12:58

質問

を実装しようとしています。 SearchView をサポートライブラリからダウンロードしました。私は、ユーザーが SearchView をフィルタリングするために List のムービーを RecyclerView .

これまでいくつかのチュートリアルに沿って、私は SearchViewActionBar が、ここから先がよくわからないのです。いくつかの例を見ましたが、どれも入力し始めると結果が表示されません。

これは私の 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>

あなたの Fragment または Activity の場合は、このメニューXMLを通常のように展開し、その中から MenuItem を含む SearchView を実装し 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() を実装し終えたら 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 ライブラリです。このライブラリは Adapter は、データセットの変更について、非常に効率的な方法で行います。唯一必要なことは、要素の順序を指定することである。これを行うには compare() メソッドにある2つの要素を比較します。 SortedList と同じように Comparator . しかし List の項目をソートするために使用されます。 RecyclerView !

SortedList と対話します。 Adapter を介して 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();
    }
}

のようなコールバックの先頭のメソッドに onMoved , onInserted のnotifyメソッドを呼び出す必要があります。 Adapter . 下部の3つのメソッド compare , areContentsTheSameareItemsTheSame は、どのようなオブジェクトをどのような順番で表示させるかによって、実装する必要があります。

それでは、これらのメソッドを一つずつ見ていきましょう。

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

これは compare() メソッドについて説明しました。この例では、単に呼び出しを Comparator で、2つのモデルを比較します。もし、画面にアルファベット順で表示させたい場合。このコンパレータは次のようになります。

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 は、change イベントを呼び出す必要があるかどうかを判断するためにこれを使用します。 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;
    }
}

補足: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を付与するのが最もシンプルな方法です。例えば、データベースからデータを照会する場合、主キーフィールドにすることができます。

を使用すると SortedList.Callback のインスタンスを作成することができます。 SortedList :

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

のコンストラクタの最初のパラメタとして、このパラメタを指定します。 SortedList には、モデルのクラスを渡す必要があります。もう一方のパラメータは、単に SortedList.Callback 上で定義した

さて、本題に入りましょう。もし Adapter を使って 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();
    }
}

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

ここでは、notifyメソッドを呼び出す必要はありません。 SortedList はすでに SortedList.Callback ! を削除する remove メソッドです。 List のモデルで構成されています。というのは SortedList は1つのオブジェクトを削除できるremoveメソッドしか持っていないので、リスト上をループしてモデルを1つずつ削除していく必要があります。呼び出し beginBatchedUpdates() に対して行うすべての変更を一括して行います。 SortedList を一緒にして、パフォーマンスを向上させます。を呼び出すと endBatchedUpdates()RecyclerView は、すべての変更について一度に通知されます。

さらに、理解しておかなければならないのは、オブジェクトを SortedList で、それがすでに SortedList は再び追加されることはありません。代わりに SortedListareContentsTheSame() メソッドを使って、オブジェクトが変更されたかどうか、そしてもしそのアイテムが 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();
}

この方法もまた、パフォーマンスを上げるためにすべての更新を一括して行います。最初のループは逆になっています。なぜなら、最初にある項目を削除すると、それ以降のすべての項目のインデックスが乱れ、場合によってはデータの不整合などの問題につながる可能性があるからです。その後で ListSortedList を使って addAll() にまだない項目をすべて追加します。 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 のすべての可能なモデルで構成されます。この例では ListExampleModel のインスタンスを、ムービーの配列から取得します。

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 . その後 List のムービー名から、モデルの 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() を渡し ListExampleModel をクエリ文字列と同じように指定します。次に replaceAll() の上で Adapter を渡し、フィルタリングされた List によって返される filter() . また scrollToPosition(0) の上で RecyclerView を使用して、ユーザーが何かを検索するときに常にすべての項目を見ることができるようにします。それ以外の場合は 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からはアイテムアニメーションが無料で使えるようになります!

これは非常に詳細な説明であり、おそらくこの全体が実際よりも複雑に見えることは承知していますが、この問題全体を一般化して Adapter をベースにした SortedList よりシンプルになりました。


問題の一般化とアダプタの簡略化

このセクションでは、あまり詳細には触れません - Stack Overflowの回答の文字数制限にぶつかっていることもありますが、ほとんどはすでに上で説明されているからです - が、変更点をまとめると、以下のようになります。私たちは、ベースとなる Adapter クラスは、すでに SortedList へのモデルのバインディングと同様に ViewHolder インスタンスを実装する便利な方法を提供します。 Adapter をベースにした SortedList . そのためには、2つのことをしなければなりません。

  • を作成する必要があります。 ViewModel インターフェイスを実装する必要があります。
  • を作成する必要があります。 ViewHolder サブクラスは bind() メソッドを使用します。 Adapter は、モデルを自動的にバインドするために使用することができます。

これによって、本来は RecyclerView を実装するだけで、モデルとそれに対応する ViewHolder を実装しています。この基本クラスを使用すると、私たちは Adapter とその SortedList .

SortedListAdapter

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

これで完了です。しかし、最後にもう一つ言っておかなければならないことがあります。この SortedListAdapter は、同じ add() , remove() または replaceAll() メソッドで、オリジナルの ExampleAdapter が持っていた。これは別の Editor オブジェクトを通してアクセスできるリストの項目を修正します。 edit() メソッドを使用します。ですから、アイテムを削除したり追加したりしたい場合は edit() で項目を追加したり削除したりします。 Editor インスタンスを作成し、完了したら commit() に変更を適用するために、その上に SortedList :

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

この方法で行ったすべての変更は、パフォーマンスを向上させるために一括して行われます。そのため replaceAll() メソッドは、上記の章で実装したこの Editor オブジェクトを作成します。

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

を呼び出すのを忘れた場合 commit() この場合、変更は一切適用されません。