Cách lọc RecyclerView bằng SearchView

335
Jacques Krause 2015-05-23 03:39.

Tôi đang cố gắng triển khai SearchViewtừ thư viện hỗ trợ. Tôi muốn người dùng sử dụng SearchViewđể lọc một Listbộ phim trong a RecyclerView.

Tôi đã theo một vài hướng dẫn cho đến nay và tôi đã thêm SearchViewvào ActionBar, nhưng tôi không thực sự chắc chắn nơi để đi từ đây. Tôi đã xem một vài ví dụ nhưng không có ví dụ nào hiển thị kết quả khi bạn bắt đầu nhập.

Đây là của tôi 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);
    }
}

Và đây là của tôi 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);
        }
    }
}

11 answers

942
Xaver Kapeller 2015-05-25 13:31.

Giới thiệu

Vì không thực sự rõ ràng trong câu hỏi của bạn chính xác là bạn đang gặp vấn đề gì, tôi đã viết hướng dẫn nhanh này về cách triển khai tính năng này; nếu bạn vẫn có câu hỏi, hãy hỏi.

Tôi có một ví dụ làm việc về mọi thứ tôi đang nói ở đây trong Kho lưu trữ GitHub này .
Nếu bạn muốn biết thêm về dự án mẫu, hãy truy cập trang chủ của dự án .

Trong mọi trường hợp, kết quả sẽ giống như sau:

Nếu lần đầu tiên bạn muốn chơi với ứng dụng demo, bạn có thể cài đặt nó từ Cửa hàng Play:

Dù sao chúng ta hãy bắt đầu.


Thiết lập SearchView

Trong thư mục res/menutạo một tệp mới có tên main_menu.xml. Trong đó thêm một mục và thiết lập actionViewClassđể android.support.v7.widget.SearchView. Vì bạn đang sử dụng thư viện hỗ trợ, bạn phải sử dụng không gian tên của thư viện hỗ trợ để đặt actionViewClassthuộc tính. Tệp xml của bạn sẽ trông giống như sau:

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

Trong của bạn Fragmenthoặc Activitybạn phải tăng xml menu này như bình thường, sau đó bạn có thể tìm trong MenuItemđó có chứa SearchViewvà triển khai OnQueryTextListenermà chúng tôi sẽ sử dụng để lắng nghe các thay đổi đối với văn bản được nhập vào 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;
}

Và bây giờ SearchViewđã sẵn sàng để sử dụng. Chúng tôi sẽ triển khai logic bộ lọc sau này onQueryTextChange()khi chúng tôi hoàn thành việc triển khai Adapter.


Thiết lập Adapter

Đầu tiên và quan trọng nhất, đây là lớp mô hình mà tôi sẽ sử dụng cho ví dụ này:

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

Nó chỉ là mô hình cơ bản của bạn sẽ hiển thị một văn bản trong RecyclerView. Đây là bố cục tôi sẽ sử dụng để hiển thị văn bản:

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

Như bạn thấy, tôi sử dụng Data Binding. Nếu bạn chưa bao giờ làm việc với dữ liệu ràng buộc trước đây, đừng nản lòng! Nó rất đơn giản và mạnh mẽ, tuy nhiên tôi không thể giải thích cách hoạt động của nó trong phạm vi của câu trả lời này.

Đây là ViewHoldercho ExampleModellớp học:

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

Một lần nữa không có gì đặc biệt. Nó chỉ sử dụng liên kết dữ liệu để liên kết lớp mô hình với bố cục này như chúng ta đã xác định trong bố cục xml ở trên.

Bây giờ cuối cùng chúng ta có thể đến với phần thực sự thú vị: Viết bộ điều hợp. Tôi sẽ bỏ qua phần triển khai cơ bản của Adaptervà thay vào đó tôi sẽ tập trung vào các phần có liên quan đến câu trả lời này.

Nhưng trước tiên có một điều chúng ta phải nói đến: SortedListLớp học.


SortedList

Đây SortedListlà một công cụ hoàn toàn tuyệt vời, là một phần của RecyclerViewthư viện. Nó quan tâm đến việc thông báo Adaptervề những thay đổi đối với tập dữ liệu và làm như vậy một cách rất hiệu quả. Điều duy nhất nó yêu cầu bạn làm là chỉ định thứ tự của các phần tử. Bạn cần làm điều đó bằng cách triển khai một compare()phương thức so sánh hai phần tử SortedListgiống như một Comparator. Nhưng thay vì sắp xếp a, Listnó được sử dụng để sắp xếp các mục trong RecyclerView!

Các SortedListtương tác với Adapterthông qua một Callbacklớp học mà bạn cần phải thực hiện:

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

Trong phương pháp ở trên cùng của callback như onMoved, onInsertedvv bạn phải gọi tương đương với phương pháp của bạn thông báo Adapter. Ba phương thức ở dưới cùng compare, areContentsTheSameareItemsTheSamebạn phải triển khai theo loại đối tượng bạn muốn hiển thị và thứ tự các đối tượng này sẽ xuất hiện trên màn hình.

Chúng ta hãy xem xét từng phương pháp sau:

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

Đây là compare()phương pháp tôi đã nói trước đó. Trong ví dụ này, tôi chỉ chuyển lệnh gọi tới một Comparatorđể so sánh hai mô hình. Nếu bạn muốn các mục xuất hiện theo thứ tự bảng chữ cái trên màn hình. Bộ so sánh này có thể trông giống như sau:

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

Bây giờ chúng ta hãy xem xét phương pháp tiếp theo:

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

Mục đích của phương pháp này là xác định xem nội dung của một mô hình có thay đổi hay không. Việc SortedListsử dụng điều này để xác định xem một sự kiện thay đổi có cần được gọi hay không - nói cách khác nếu sự kiện này có thể vượt qua RecyclerViewphiên bản cũ và mới. Nếu bạn mô hình các lớp có một cách thực hiện đúng equals()hashCode()bạn thường có thể thực hiện nó như trên. Nếu chúng ta thêm một equals()hashCode()triển khai vào ExampleModellớp, nó sẽ trông giống như sau:

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

Lưu ý nhanh: Hầu hết các IDE như Android Studio, IntelliJ và Eclipse đều có chức năng tạo equals()hashCode()triển khai cho bạn chỉ bằng một nút bấm! Vì vậy, bạn không cần phải tự mình thực hiện chúng. Tra cứu trên internet xem nó hoạt động như thế nào trong IDE của bạn!

Bây giờ chúng ta hãy xem xét phương pháp cuối cùng:

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

Các SortedListsử dụng phương pháp này để kiểm tra xem hai mục tham khảo các điều tương tự. Theo thuật ngữ đơn giản nhất (không giải thích cách thức SortedListhoạt động), điều này được sử dụng để xác định xem một đối tượng đã được chứa trong Listvà nếu một hoạt ảnh thêm, di chuyển hoặc thay đổi cần được phát. Nếu mô hình của bạn có id, bạn thường chỉ so sánh id trong phương pháp này. Nếu họ không, bạn cần phải tìm ra một số cách khác để kiểm tra điều này, nhưng bạn có triển khai được điều này hay không tùy thuộc vào ứng dụng cụ thể của bạn. Thông thường, đó là tùy chọn đơn giản nhất để cấp cho tất cả các mô hình một id - ví dụ có thể là trường khóa chính nếu bạn đang truy vấn dữ liệu từ cơ sở dữ liệu.

Với việc SortedList.Callbacktriển khai chính xác, chúng ta có thể tạo một phiên bản của SortedList:

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

Là tham số đầu tiên trong phương thức khởi tạo, SortedListbạn cần truyền lớp mô hình của mình. Các tham số khác chỉ là SortedList.Callbackchúng tôi đã xác định ở trên.

Bây giờ chúng ta hãy bắt đầu kinh doanh: Nếu chúng ta triển khai Adaptervới một SortedListnó sẽ trông giống như sau:

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

Mục Comparatorđược sử dụng để sắp xếp mục được chuyển vào thông qua hàm tạo nên chúng ta có thể sử dụng cùng một thứ Adapterngay cả khi các mục được cho là hiển thị theo một thứ tự khác.

Bây giờ chúng ta gần như đã hoàn thành! Nhưng trước tiên chúng ta cần một cách để thêm hoặc xóa các mục vào Adapter. Vì mục đích này, chúng tôi có thể thêm các phương thức Adaptercho phép chúng tôi thêm và xóa các mục vào 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();
}

Chúng tôi không cần gọi bất kỳ phương thức thông báo nào ở đây bởi vì phương thức này SortedListđã thực hiện điều này thông qua SortedList.Callback! Bên cạnh đó, việc triển khai các phương pháp này khá dễ dàng với một ngoại lệ: phương thức remove loại bỏ một Listtrong các mô hình. Vì SortedListchỉ có một phương thức remove có thể xóa một đối tượng duy nhất nên chúng ta cần lặp lại danh sách và xóa từng mô hình một. Gọi beginBatchedUpdates()ngay từ đầu sẽ tổng hợp tất cả những thay đổi mà chúng tôi sẽ thực hiện SortedListcùng nhau và cải thiện hiệu suất. Khi chúng tôi gọi endBatchedUpdates(), RecyclerViewđược thông báo về tất cả các thay đổi cùng một lúc.

Ngoài ra, điều bạn phải hiểu là nếu bạn thêm một đối tượng vào SortedListvà nó đã có trong SortedListnó thì nó sẽ không được thêm nữa. Thay vào đó, phương thức này SortedListsử dụng areContentsTheSame()phương thức để tìm xem đối tượng có thay đổi hay không - và nếu nó có mục trong RecyclerViewsẽ được cập nhật.

Dù sao, những gì tôi thường thích là một phương pháp cho phép tôi thay thế tất cả các mục RecyclerViewcùng một lúc. Xóa mọi thứ không có trong Listvà thêm tất cả các mục bị thiếu khỏi 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();
}

Phương pháp này lại gộp tất cả các bản cập nhật lại với nhau để tăng hiệu suất. Vòng lặp đầu tiên ngược lại vì việc xóa một mục ngay từ đầu sẽ làm rối tung chỉ mục của tất cả các mục xuất hiện sau nó và điều này có thể dẫn đến một số trường hợp dẫn đến các vấn đề như mâu thuẫn dữ liệu. Sau đó, chúng tôi chỉ thêm Listvào việc SortedListsử dụng addAll()để thêm tất cả các mục chưa có trong SortedListvà - giống như tôi đã mô tả ở trên - cập nhật tất cả các mục đã có trong SortedListnhưng đã thay đổi.

Và với điều đó Adapterlà hoàn thành. Toàn bộ sự việc sẽ giống như sau:

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

Điều duy nhất còn thiếu bây giờ là thực hiện lọc!


Triển khai logic bộ lọc

Để thực hiện logic bộ lọc, trước tiên chúng ta phải xác định một Listtrong tất cả các mô hình có thể có. Đối với ví dụ này, tôi tạo một Listsố ExampleModelphiên bản từ một loạt các phim:

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

Không có gì đặc biệt xảy ra ở đây, chúng tôi chỉ tạo Adaptervà đặt nó thành RecyclerView. Sau đó, chúng tôi tạo một Listmô hình từ tên phim trong MOVIESmảng. Sau đó, chúng tôi thêm tất cả các mô hình vào SortedList.

Bây giờ chúng ta có thể quay lại onQueryTextChange()nơi chúng ta đã xác định trước đó và bắt đầu triển khai logic bộ lọc:

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

Điều này một lần nữa khá thẳng về phía trước. Chúng tôi gọi phương thức filter()và chuyển vào Listcủa ExampleModels cũng như chuỗi truy vấn. Sau đó chúng tôi gọi replaceAll()vào Adaptervà vượt qua trong lọc Listđược trả về bởi filter(). Chúng tôi cũng phải gọi scrollToPosition(0)về RecyclerViewđể đảm bảo rằng người dùng luôn có thể xem tất cả các mục khi tìm kiếm một cái gì đó. Nếu không, nó RecyclerViewcó thể ở vị trí cuộn xuống trong khi lọc và sau đó ẩn một vài mục. Cuộn lên đầu đảm bảo trải nghiệm người dùng tốt hơn khi tìm kiếm.

Điều duy nhất còn lại cần làm bây giờ là triển khai filter()chính nó:

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

Điều đầu tiên chúng ta làm ở đây là gọi toLowerCase()chuỗi truy vấn. Chúng tôi không muốn hàm tìm kiếm của mình phân biệt chữ hoa chữ thường và bằng cách gọi toLowerCase()tất cả các chuỗi mà chúng tôi so sánh, chúng tôi có thể đảm bảo rằng chúng tôi trả về cùng một kết quả bất kể chữ hoa / thường. Sau đó, nó chỉ cần lặp lại tất cả các mô hình trong mô hình mà Listchúng tôi đã truyền vào nó và kiểm tra xem chuỗi truy vấn có trong văn bản của mô hình hay không. Nếu đúng thì mô hình sẽ được thêm vào bộ lọc List.

Và đó là nó! Đoạn mã trên sẽ chạy trên API cấp 7 trở lên và bắt đầu với API cấp 11, bạn sẽ nhận được hình ảnh động mặt hàng miễn phí!

Tôi nhận ra rằng đây là một mô tả rất chi tiết có thể làm cho toàn bộ vấn đề này có vẻ phức tạp hơn thực tế, nhưng có một cách chúng ta có thể tổng quát hóa toàn bộ vấn đề này và làm cho việc triển khai Adapterdựa trên SortedListđơn giản hơn nhiều.


Khái quát vấn đề và đơn giản hóa Bộ điều hợp

Trong phần này, tôi sẽ không đi vào chi tiết nhiều - một phần vì tôi đang chạy ngược lại giới hạn ký tự cho câu trả lời trên Stack Overflow nhưng cũng vì hầu hết nó đã được giải thích ở trên - nhưng để tóm tắt các thay đổi: Chúng ta có thể triển khai một Adapterlớp cơ sở trong đó đã xử lý các SortedListmô hình cũng như ràng buộc với các ViewHoldercá thể và cung cấp một cách thuận tiện để triển khai một Adapterdựa trên a SortedList. Muốn vậy chúng ta phải làm hai điều:

  • Chúng ta cần tạo một ViewModelgiao diện mà tất cả các lớp mô hình phải triển khai
  • Chúng ta cần tạo một ViewHolderlớp con định nghĩa một bind()phương thức Adaptercó thể sử dụng để liên kết các mô hình một cách tự động.

Điều này cho phép chúng tôi chỉ tập trung vào nội dung được cho là sẽ được hiển thị RecyclerViewbằng cách chỉ triển khai các mô hình và có các ViewHoldertriển khai tương ứng . Sử dụng lớp cơ sở này, chúng ta không phải lo lắng về các chi tiết phức tạp của Adaptervà của nó SortedList.

SortedListAdapter

Do giới hạn ký tự cho câu trả lời trên StackOverflow, tôi không thể đi qua từng bước triển khai lớp cơ sở này hoặc thậm chí thêm mã nguồn đầy đủ ở đây, nhưng bạn có thể tìm thấy mã nguồn đầy đủ của lớp cơ sở này - tôi đã gọi nó SortedListAdapter- trong này GitHub Gist .

Để làm cho cuộc sống của bạn trở nên đơn giản, tôi đã xuất bản một thư viện trên jCenter có chứa SortedListAdapter! Nếu bạn muốn sử dụng nó thì tất cả những gì bạn cần làm là thêm phần phụ thuộc này vào tệp build.gradle của ứng dụng:

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

Bạn có thể tìm thêm thông tin về thư viện này trên trang chủ của thư viện .

Sử dụng SortedListAdapter

Để sử dụng, SortedListAdapterchúng tôi phải thực hiện hai thay đổi:

  • Thay đổi ViewHolderđể nó mở rộng SortedListAdapter.ViewHolder. Tham số kiểu phải là mô hình được ràng buộc với điều này ViewHolder- trong trường hợp này ExampleModel. Bạn phải liên kết dữ liệu với các mô hình của mình performBind()thay vì 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);
        }
    }
    
  • Đảm bảo rằng tất cả các mô hình của bạn đều triển khai ViewModelgiao diện:

    public class ExampleModel implements SortedListAdapter.ViewModel {
        ...
    }
    

Sau đó, chúng tôi chỉ cần cập nhật ExampleAdapterđể mở rộng SortedListAdaptervà xóa mọi thứ chúng tôi không cần nữa. Tham số kiểu phải là kiểu mô hình bạn đang làm việc - trong trường hợp này ExampleModel. Nhưng nếu bạn đang làm việc với các loại mô hình khác nhau thì hãy đặt tham số kiểu thành 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);
    }
}

Sau đó chúng tôi đã hoàn thành! Tuy nhiên, một điều cuối cùng cần đề cập: Phương thức SortedListAdapterkhông giống nhau add(), remove()hoặc replaceAll()các phương pháp ban đầu của chúng tôi ExampleAdaptercó. Nó sử dụng một Editorđối tượng riêng biệt để sửa đổi các mục trong danh sách có thể được truy cập thông qua edit()phương thức. Vì vậy, nếu bạn muốn xóa hoặc thêm các mục bạn phải gọi edit()thì hãy thêm và xóa các mục trên phiên bản này Editorvà khi bạn đã hoàn tất, hãy gọi commit()nó để áp dụng các thay đổi cho SortedList:

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

Tất cả các thay đổi bạn thực hiện theo cách này được gộp lại với nhau để tăng hiệu suất. Các replaceAll()phương pháp chúng tôi thực hiện trong các chương ở trên cũng có mặt trên này Editorđối tượng:

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

Nếu bạn quên gọi commit()thì sẽ không có thay đổi nào của bạn được áp dụng!

211
klimat 2016-06-01 21:50.

Tất cả những gì bạn cần làm là thêm filterphương thức vào RecyclerView.Adapter:

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được khởi tạo trong hàm tạo của bộ điều hợp như itemsCopy.addAll(items).

Nếu bạn làm như vậy, chỉ cần gọi filtertừ OnQueryTextListener:

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

Đó là một ví dụ từ việc lọc danh bạ của tôi theo tên và số điện thoại.

83
sagits 2016-06-10 10:29.

Theo dõi @Shruthi Kamoji một cách rõ ràng hơn, chúng ta chỉ có thể sử dụng một tệp có thể lọc, nó có nghĩa là:

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

Chữ E ở đây là Kiểu Chung, bạn có thể mở rộng nó bằng cách sử dụng lớp của mình:

public class customerAdapter extends GenericRecycleAdapter<CustomerModel>

Hoặc chỉ cần thay đổi chữ E thành loại bạn muốn ( <CustomerModel>ví dụ)

Sau đó, từ searchView (tiện ích bạn có thể đặt trên 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;
    }
});
5
AndroidGeek 2017-08-19 01:10.

chỉ cần tạo hai danh sách trong bộ điều hợp một gốc và một tạm thời và thực hiện Filterable .

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                final FilterResults oReturn = new FilterResults();
                final ArrayList<T> results = new ArrayList<>();
                if (origList == null)
                    origList = new ArrayList<>(itemList);
                if (constraint != null && constraint.length() > 0) {
                    if (origList != null && origList.size() > 0) {
                        for (final T cd : origList) {
                            if (cd.getAttributeToSearch().toLowerCase()
                                    .contains(constraint.toString().toLowerCase()))
                                results.add(cd);
                        }
                    }
                    oReturn.values = results;
                    oReturn.count = results.size();//newly Aded by ZA
                } else {
                    oReturn.values = origList;
                    oReturn.count = origList.size();//newly added by ZA
                }
                return oReturn;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(final CharSequence constraint,
                                          FilterResults results) {
                itemList = new ArrayList<>((ArrayList<T>) results.values);
                // FIXME: 8/16/2017 implement Comparable with sort below
                ///Collections.sort(itemList);
                notifyDataSetChanged();
            }
        };
    }

Ở đâu

public GenericBaseAdapter(Context mContext, List<T> itemList) {
        this.mContext = mContext;
        this.itemList = itemList;
        this.origList = itemList;
    }
5
Firoz Ahmed 2017-08-31 05:06.

Trong Bộ điều hợp:

public void setFilter(List<Channel> newList){
        mChannels = new ArrayList<>();
        mChannels.addAll(newList);
        notifyDataSetChanged();
    }

Đang hoạt động:

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                newText = newText.toLowerCase();
                ArrayList<Channel> newList = new ArrayList<>();
                for (Channel channel: channels){
                    String channelName = channel.getmChannelName().toLowerCase();
                    if (channelName.contains(newText)){
                        newList.add(channel);
                    }
                }
                mAdapter.setFilter(newList);
                return true;
            }
        });
3
Panos Gr 2018-11-04 08:08.

Với Thành phần kiến ​​trúc Android thông qua việc sử dụng LiveData, điều này có thể dễ dàng thực hiện với bất kỳ loại Bộ điều hợp nào . Bạn chỉ cần thực hiện các bước sau:

1. Thiết lập dữ liệu của bạn để trả về từ Cơ sở dữ liệu phòng dưới dạng LiveData như trong ví dụ dưới đây:

@Dao
public interface CustomDAO{

@Query("SELECT * FROM words_table WHERE column LIKE :searchquery")
    public LiveData<List<Word>> searchFor(String searchquery);
}

2. Tạo đối tượng ViewModel để cập nhật dữ liệu của bạn trực tiếp thông qua một phương pháp sẽ kết nối DAOgiao diện người dùng của bạn

public class CustomViewModel extends AndroidViewModel {

    private final AppDatabase mAppDatabase;

    public WordListViewModel(@NonNull Application application) {
        super(application);
        this.mAppDatabase = AppDatabase.getInstance(application.getApplicationContext());
    }

    public LiveData<List<Word>> searchQuery(String query) {
        return mAppDatabase.mWordDAO().searchFor(query);
    }

}

3. Gọi dữ liệu của bạn từ ViewModel bằng cách chuyển vào truy vấn thông qua onQueryTextListener như bên dưới:

Bên trong onCreateOptionsMenuthiết lập trình nghe của bạn như sau

searchView.setOnQueryTextListener(onQueryTextListener);

Thiết lập trình nghe truy vấn của bạn ở đâu đó trong lớp SearchActivity của bạn như sau

private android.support.v7.widget.SearchView.OnQueryTextListener onQueryTextListener =
            new android.support.v7.widget.SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    getResults(query);
                    return true;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    getResults(newText);
                    return true;
                }

                private void getResults(String newText) {
                    String queryText = "%" + newText + "%";
                    mCustomViewModel.searchQuery(queryText).observe(
                            SearchResultsActivity.this, new Observer<List<Word>>() {
                                @Override
                                public void onChanged(@Nullable List<Word> words) {
                                    if (words == null) return;
                                    searchAdapter.submitList(words);
                                }
                            });
                }
            };

Lưu ý : Bước (1.) và (2.) là triển khai AAC ViewModelDAO tiêu chuẩn , "phép thuật" thực sự duy nhất đang diễn ra ở đây là OnQueryTextListener sẽ cập nhật động kết quả danh sách của bạn khi văn bản truy vấn thay đổi.

Nếu bạn cần làm rõ hơn về vấn đề này, vui lòng hỏi. Tôi hy vọng điều này đã giúp :).

1
AhmadF 2019-06-04 06:46.

Đây là cách tôi mở rộng câu trả lời @klimat để không làm mất hoạt ảnh lọc.

public void filter(String query){
    int completeListIndex = 0;
    int filteredListIndex = 0;
    while (completeListIndex < completeList.size()){
        Movie item = completeList.get(completeListIndex);
        if(item.getName().toLowerCase().contains(query)){
            if(filteredListIndex < filteredList.size()) {
                Movie filter = filteredList.get(filteredListIndex);
                if (!item.getName().equals(filter.getName())) {
                    filteredList.add(filteredListIndex, item);
                    notifyItemInserted(filteredListIndex);
                }
            }else{
                filteredList.add(filteredListIndex, item);
                notifyItemInserted(filteredListIndex);
            }
            filteredListIndex++;
        }
        else if(filteredListIndex < filteredList.size()){
            Movie filter = filteredList.get(filteredListIndex);
            if (item.getName().equals(filter.getName())) {
                filteredList.remove(filteredListIndex);
                notifyItemRemoved(filteredListIndex);
            }
        }
        completeListIndex++;
    }
}

Về cơ bản những gì nó làm là xem qua một danh sách hoàn chỉnh và thêm / bớt từng mục vào danh sách đã lọc.

1
Alaa M. 2020-07-17 07:13.

Tôi không biết tại sao mọi người lại sử dụng 2 bản sao của cùng một danh sách để giải quyết việc này. Điều này sử dụng quá nhiều RAM ...

Tại sao không chỉ ẩn các phần tử không được tìm thấy, và chỉ cần lưu trữ chỉ mục của chúng trong một Setđể có thể khôi phục chúng sau này? Đó là ít RAM hơn nhiều, đặc biệt nếu các đối tượng của bạn khá lớn.

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.SampleViewHolders>{
    private List<MyObject> myObjectsList; //holds the items of type MyObject
    private Set<Integer> foundObjects; //holds the indices of the found items

    public MyRecyclerViewAdapter(Context context, List<MyObject> myObjectsList)
    {
        this.myObjectsList = myObjectsList;
        this.foundObjects = new HashSet<>();
        //first, add all indices to the indices set
        for(int i = 0; i < this.myObjectsList.size(); i++)
        {
            this.foundObjects.add(i);
        }
    }

    @NonNull
    @Override
    public SampleViewHolders onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View layoutView = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.my_layout_for_staggered_grid, null);
        MyRecyclerViewAdapter.SampleViewHolders rcv = new MyRecyclerViewAdapter.SampleViewHolders(layoutView);
        return rcv;
    }

    @Override
    public void onBindViewHolder(@NonNull SampleViewHolders holder, int position)
    {
        //look for object in O(1) in the indices set
        if(!foundObjects.contains(position))
        {
            //object not found => hide it.
            holder.hideLayout();
            return;
        }
        else
        {
            //object found => show it.
            holder.showLayout();
        }

        //holder.imgImageView.setImageResource(...)
        //holder.nameTextView.setText(...)
    }

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

    public void findObject(String text)
    {
        //look for "text" in the objects list
        for(int i = 0; i < myObjectsList.size(); i++)
        {
            //if it's empty text, we want all objects, so just add it to the set.
            if(text.length() == 0)
            {
                foundObjects.add(i);
            }
            else
            {
                //otherwise check if it meets your search criteria and add it or remove it accordingly
                if (myObjectsList.get(i).getName().toLowerCase().contains(text.toLowerCase()))
                {
                    foundObjects.add(i);
                }
                else
                {
                    foundObjects.remove(i);
                }
            }
        }
        notifyDataSetChanged();
    }

    public class SampleViewHolders extends RecyclerView.ViewHolder implements View.OnClickListener
    {
        public ImageView imgImageView;
        public TextView nameTextView;

        private final CardView layout;
        private final CardView.LayoutParams hiddenLayoutParams;
        private final CardView.LayoutParams shownLayoutParams;

        
        public SampleViewHolders(View itemView)
        {
            super(itemView);
            itemView.setOnClickListener(this);
            imgImageView = (ImageView) itemView.findViewById(R.id.some_image_view);
            nameTextView = (TextView) itemView.findViewById(R.id.display_name_textview);

            layout = itemView.findViewById(R.id.card_view); //card_view is the id of my androidx.cardview.widget.CardView in my xml layout
            //prepare hidden layout params with height = 0, and visible layout params for later - see hideLayout() and showLayout()
            hiddenLayoutParams = new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            hiddenLayoutParams.height = 0;
            shownLayoutParams = new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        @Override
        public void onClick(View view)
        {
            //implement...
        }

        private void hideLayout() {
            //hide the layout
            layout.setLayoutParams(hiddenLayoutParams);
        }

        private void showLayout() {
            //show the layout
            layout.setLayoutParams(shownLayoutParams);
        }
    }
}

Và tôi chỉ đơn giản có một EditTexthộp tìm kiếm của mình:

cardsSearchTextView.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                myViewAdapter.findObject(editable.toString().toLowerCase());
            }
        });

Kết quả:

0
Shruthi Kamoji 2015-07-10 02:41.

Tôi đã giải quyết vấn đề tương tự bằng cách sử dụng liên kết với một số sửa đổi trong đó. Bộ lọc tìm kiếm trên RecyclerView bằng Thẻ. Nó thậm chí có thể? (hi vọng điêu nay co ich).

Đây là lớp bộ điều hợp của tôi

public class ContactListRecyclerAdapter extends RecyclerView.Adapter<ContactListRecyclerAdapter.ContactViewHolder> implements Filterable {

Context mContext;
ArrayList<Contact> customerList;
ArrayList<Contact> parentCustomerList;


public ContactListRecyclerAdapter(Context context,ArrayList<Contact> customerList)
{
    this.mContext=context;
    this.customerList=customerList;
    if(customerList!=null)
    parentCustomerList=new ArrayList<>(customerList);
}

   // other overrided methods

@Override
public Filter getFilter() {
    return new FilterCustomerSearch(this,parentCustomerList);
}
}

// Lớp lọc

import android.widget.Filter;
import java.util.ArrayList;


public class FilterCustomerSearch extends Filter
{
private final ContactListRecyclerAdapter mAdapter;
ArrayList<Contact> contactList;
ArrayList<Contact> filteredList;

public FilterCustomerSearch(ContactListRecyclerAdapter mAdapter,ArrayList<Contact> contactList) {
    this.mAdapter = mAdapter;
    this.contactList=contactList;
    filteredList=new ArrayList<>();
}

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    filteredList.clear();
    final FilterResults results = new FilterResults();

    if (constraint.length() == 0) {
        filteredList.addAll(contactList);
    } else {
        final String filterPattern = constraint.toString().toLowerCase().trim();

        for (final Contact contact : contactList) {
            if (contact.customerName.contains(constraint)) {
                filteredList.add(contact);
            }
            else if (contact.emailId.contains(constraint))
            {
                filteredList.add(contact);

            }
            else if(contact.phoneNumber.contains(constraint))
                filteredList.add(contact);
        }
    }
    results.values = filteredList;
    results.count = filteredList.size();
    return results;
}

@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    mAdapter.customerList.clear();
    mAdapter.customerList.addAll((ArrayList<Contact>) results.values);
    mAdapter.notifyDataSetChanged();
}

}

// Lớp hoạt động

public class HomeCrossFadeActivity extends AppCompatActivity implements View.OnClickListener,OnFragmentInteractionListener,OnTaskCompletedListner
{
Fragment fragment;
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_homecrossfadeslidingpane2);CardView mCard;
   setContentView(R.layout.your_main_xml);}
   //other overrided methods
  @Override
   public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.

    MenuInflater inflater = getMenuInflater();
    // Inflate menu to add items to action bar if it is present.
    inflater.inflate(R.menu.menu_customer_view_and_search, menu);
    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.menu_search).getActionView();
    searchView.setQueryHint("Search Customer");
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            if(fragment instanceof CustomerDetailsViewWithModifyAndSearch)
                ((CustomerDetailsViewWithModifyAndSearch)fragment).adapter.getFilter().filter(newText);
            return false;
        }
    });



    return true;
}
}

Trong phương thức OnQueryTextChangeListener (), hãy sử dụng bộ điều hợp của bạn. Tôi đã cast nó thành phân mảnh vì adpter của tôi bị phân mảnh. Bạn có thể sử dụng bộ điều hợp trực tiếp nếu nó nằm trong lớp hoạt động của bạn.

0
toidv 2016-08-11 10:37.

Tôi khuyên bạn nên sửa đổi giải pháp của @Xaver Kapeller với 2 điều bên dưới để tránh sự cố sau khi bạn xóa văn bản được tìm kiếm (bộ lọc không hoạt động nữa) do danh sách phía sau của bộ điều hợp có kích thước nhỏ hơn danh sách bộ lọc và đã xảy ra lỗi IndexOutOfBoundsException. Vì vậy, mã cần phải sửa đổi như bên dưới

public void addItem(int position, ExampleModel model) {
    if(position >= mModel.size()) {
        mModel.add(model);
        notifyItemInserted(mModel.size()-1);
    } else {
        mModels.add(position, model);
        notifyItemInserted(position);
    }
}

Và cũng sửa đổi trong chức năng moveItem

public void moveItem(int fromPosition, int toPosition) {
    final ExampleModel model = mModels.remove(fromPosition);
    if(toPosition >= mModels.size()) {
        mModels.add(model);
        notifyItemMoved(fromPosition, mModels.size()-1);
    } else {
        mModels.add(toPosition, model);
        notifyItemMoved(fromPosition, toPosition); 
    }
}

Hy vọng rằng nó có thể giúp bạn!

0
Richard Kamere 2019-12-18 00:22.

Thêm giao diện trong bộ điều hợp của bạn.

public interface SelectedUser{

    void selectedUser(UserModel userModel);

}

triển khai giao diện trong hoạt động của bạn và ghi đè phương thức. @Override public void selectUser (UserModel userModel) {

    startActivity(new Intent(MainActivity.this, SelectedUserActivity.class).putExtra("data",userModel));



}

Hướng dẫn đầy đủ và mã nguồn: Recyclerview with searchview và onclicklistener

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

Làm thế nào để xây dựng một quả địa cầu từ Scratch

Làm thế nào để xây dựng một quả địa cầu từ Scratch

Thời gian, sự kiên nhẫn, thời gian, sự cống hiến và thời gian chỉ là một vài trong số những thứ mà Peter Bellerby cần để thành lập và sau đó điều hành Bellerby & Co. Globemakers, một trong những công ty duy nhất trên Trái đất vẫn sản xuất các quả địa cầu bằng tay.

Năm giai đoạn đau buồn sau khi mất việc làm

Năm giai đoạn đau buồn sau khi mất việc làm

Đó là một ngày thứ Bảy, máy bay của tôi hạ cánh, và tôi đã sẵn sàng để thư giãn trong một kỳ nghỉ cuối tuần ngắn ngủi, khi một email đến trên điện thoại của tôi. Tôi đã mất việc.

Giữ an toàn cho danh tính của bạn với một cảnh báo đóng băng hoặc gian lận tín dụng

Giữ an toàn cho danh tính của bạn với một cảnh báo đóng băng hoặc gian lận tín dụng

Nếu bạn đã từng bị đánh cắp danh tính của mình, bạn biết đó là một trải nghiệm đáng sợ và căng thẳng. Một cách không phổ biến để ngăn chặn nó? Tín dụng bị đóng băng.

Buổi ra mắt phần 3 của The Good Place có một học sinh mới: Khán giả

Buổi ra mắt phần 3 của The Good Place có một học sinh mới: Khán giả

Eleanor (Kristen Bell) dường như đã tình nguyện cho chúng tôi làm vật tưởng nhớ. NBC's The Good Place là một chương trình thích thử thách tốt.

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Thanh thiếu niên, Gia đình Florida Hội đồng quản trị trường học về Luật 'Không nói đồng tính': 'Buộc chúng tôi tự kiểm duyệt'

Vụ kiện, nêu tên một số học khu, lập luận rằng dự luật "Không nói đồng tính" được ban hành gần đây của Florida "có hiệu quả im lặng và xóa bỏ học sinh và gia đình LGBTQ +"

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Khi bạn không thể trở thành người mà Internet muốn bạn trở thành

Tôi ghét từ "tàu đắm". Mọi người cảm thấy thoải mái trong la bàn đạo đức của riêng mình, và khi làm như vậy, họ thấy mình vượt qua sự phán xét.

Language