Giới thiệu
Vì nó không thực sự rõ ràng từ 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 lên 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òn thắc mắc hãy hỏi.
Tôi có một ví dụ hoạt động về tất cả 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 ví dụ, hãy truy cập trang chủ của dự án .
Trong mọi trường hợp, kết quả sẽ trông giống như thế này:
Nếu trước tiên bạn muốn chơi xung quanh với ứng dụng demo, bạn có thể cài đặt nó từ Cửa hàng Play:
Dù sao đi nữa, hãy bắt đầu.
Thiết lập SearchView
Trong thư mục res/menu
tạo một tập tin mới gọi là 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 actionViewClass
thuộc tính. Tệp xml của bạn sẽ trông giống như thế này:
<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 Fragment
hoặc Activity
bạn phải tăng xml menu này như bình thường, sau đó bạn có thể tìm cái MenuItem
chứa SearchView
và thực hiện cái OnQueryTextListener
mà chúng ta sẽ sử dụng để lắng nghe những thay đổi của 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 để được sử dụng. Chúng tôi sẽ triển khai logic bộ lọc sau này sau onQueryTextChange()
khi 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;
}
}
Đây 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 ràng buộc dữ liệu trước khi đừ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 nó hoạt động như thế nào trong phạm vi của câu trả lời này.
Đây là ViewHolder
đối với ExampleModel
lớp:
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);
}
}
Lại 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 đã định nghĩa trong bố cục xml ở trên.
Bây giờ chúng ta cuối cùng cũng có thể đến phần thực sự thú vị: Viết Adaptor. Tôi sẽ bỏ qua việc thực hiện cơ bản Adapter
và thay vào đó 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 về: SortedList
Lớp học.
Danh sách được sắp xếp
Đây SortedList
là một công cụ hoàn toàn tuyệt vời là một phần của RecyclerView
thư viện. Nó quan tâm đến việc thông báo Adapter
về các thay đổi về tập dữ liệu và làm như vậy nó là 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 một thứ tự của các yếu tố. Bạn cần phải làm điều đó bằng cách thực hiện một compare()
phương thức so sánh hai yếu tố SortedList
giống như a Comparator
. Nhưng thay vì sắp xếp List
nó được sử dụng để sắp xếp các mục trong RecyclerView
!
Các SortedList
tương tác với Adapter
thông qua một Callback
lớp mà bạ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
, onInserted
vv 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
, areContentsTheSame
vàareItemsTheSame
bạn phải thực hiện theo những gì loại của các đối tượng bạn muốn hiển thị và theo 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 thực hiện từng phương pháp một:
@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 cuộc gọi đến 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 như thế này:
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ờ hãy xem 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 SortedList
sử dụng điều này để xác định xem một sự kiện thay đổi có cần phải được gọi hay không - nói cách khác là liệu có RecyclerView
nên vượt qua phiên bản cũ và mới hay không. Nếu bạn mô hình các lớp có một chính xác equals()
và hashCode()
thực hiện, bạn thường có thể thực hiện nó như trên. Nếu chúng ta thêm một equals()
và hashCode()
thực hiện vào ExampleModel
lớp, nó sẽ trông giống như thế này:
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()
và hashCode()
triển khai cho bạn chỉ bằng cách nhấn nút! Vì vậy, bạn không phải tự thực hiện chúng. Tra cứu trên internet cách thức hoạt động trong IDE của bạn!
Bây giờ chúng ta hãy xem phương pháp cuối cùng:
@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
return item1.getId() == item2.getId();
}
Việc SortedList
sử dụng phương pháp này để kiểm tra nếu hai mục đề cập đến cùng một điều. Nói một cách đơn giản nhất (không giải thích cách thức SortedList
hoạt động), điều này được sử dụng để xác định xem một đối tượng đã được chứa trongList
và nếu cần thêm, di chuyển hoặc thay đổi hình ảnh động cần phải đượ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 thức này. Nếu họ không cần bạn phải tìm ra một số cách khác để kiểm tra điều này, nhưng cuối cùng bạn thực hiện điều này phụ thuộc vào ứng dụng cụ thể của bạn. Thông thường, đây là tùy chọn đơn giản nhất để cung cấp cho tất cả các mô hình 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.Callback
thực hiện chính xác, chúng ta có thể tạo một thể hiện của SortedList
:
final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);
Là tham số đầu tiên trong hàm tạo của SortedList
bạn cần vượt qua lớp mô hình của bạn. Các tham số khác chỉ là SortedList.Callback
chúng tôi xác định ở trên.
Bây giờ chúng ta hãy bắt tay vào kinh doanh: Nếu chúng ta thực hiện Adapter
với SortedList
nó thì nó sẽ trông giống như thế này:
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();
}
}
Việc Comparator
sử dụng để sắp xếp mục được truyền qua hàm tạo để chúng ta có thể sử dụng tương tự Adapter
ngay cả khi các mục được cho là được hiển thị theo thứ tự khác.
Bây giờ chúng ta gần như đã hoàn tất! 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ới mục đích này, chúng tôi có thể thêm các phương thức vào Adapter
đó cho 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 phải gọi bất kỳ phương thức thông báo nào ở đây vì SortedList
đã thực hiện điều này thông qua SortedList.Callback
! Bên cạnh đó, việc thực hiện các phương thức này khá đơn giản với một ngoại lệ: phương thức remove loại bỏ một List
mô hình. Vì SortedList
chỉ có một phương thức loại bỏ có thể loại bỏ một đối tượng duy nhất, chúng ta cần lặp qua danh sách và loại bỏ từng mô hình một. Gọi beginBatchedUpdates()
vào các đợt đầu tiên tất cả các thay đổi chúng ta sẽ thực hiện SortedList
cùng nhau và cải thiện hiệu suất. Khi chúng tôi gọi endBatchedUpdates()
thì 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 SortedList
và nó đã ở trong SortedList
đó thì nó sẽ không được thêm lại. Thay vào đó, phương thức SortedList
sử dụng areContentsTheSame()
phương pháp để tìm hiểu xem đối tượng đã thay đổi chưa - và nếu nó có mục trong đó RecyclerView
sẽ đượ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 RecyclerView
cùng một lúc. Xóa mọi thứ không có trong List
và thêm tất cả các mục bị thiếu trong 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 một lần nữa bó 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 là ngược lại vì việc loại bỏ một mục khi bắt đầu sẽ làm rối loạn các 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 gặp vấn đề như sự không nhất quán dữ liệu. Sau đó, chúng tôi chỉ cần thêm List
vào việc SortedList
sử dụng addAll()
để thêm tất cả các mục chưa có trong SortedList
và - giống như tôi đã mô tả ở trên - cập nhật tất cả các mục đã có trong SortedList
nhưng đã thay đổi.
Và với điều đó Adapter
là hoàn thành. Toàn bộ mọi thứ sẽ trông giống như thế này:
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!
Thực hiện 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 List
trong tất cả các mô hình có thể. Ví dụ này tôi tạo ra một List
trong ExampleModel
các trường hợp 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ỉ cần khởi tạo Adapter
và đặt nó thành RecyclerView
. Sau đó, chúng tôi tạo ra một List
mô hình từ các tên phim trong MOVIES
mả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()
cái mà 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;
}
Đây là 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à vượt qua trong List
các ExampleModel
s cũng như chuỗi truy vấn. Sau đó chúng tôi gọi replaceAll()
vào Adapter
và 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, RecyclerView
có thể ở vị trí cuộn xuống trong khi lọc và sau đó ẩn một vài mục. Cuộn lên trên cùng đảm bảo trải nghiệm người dùng tốt hơn trong khi tìm kiếm.
Điều duy nhất còn lại phải làm bây giờ là filter()
tự thực hiệ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()
trên chuỗi truy vấn. Chúng tôi không muốn chức năng tìm kiếm của chúng tôi phân biệt chữ hoa chữ thường và bằng cách gọi toLowerCase()
tất cả các chuỗi 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ể trường hợp nào. Sau đó, nó chỉ lặp qua tất cả các mô hình trong List
chúng ta đã truyền vào nó và kiểm tra xem chuỗi truy vấn có được chứa trong văn bản của mô hình không. Nếu có thì mô hình đượ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ẽ có được hoạt hình vật phẩm 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ộ điều 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ể khái quát toàn bộ vấn đề này và thực hiện Adapter
dựa trên SortedList
đơn giản hơn nhiều.
Tổng quát hóa vấn đề và đơn giản hóa Bộ điều hợp
Trong phần này tôi sẽ không đi sâu vào chi tiết - một phần vì tôi đang chạy ngược lại giới hạn ký tự cho các câu trả lời trên Stack Overflow mà còn bởi vì hầu hết đã được giải thích ở trên - nhưng để tóm tắt các thay đổi: Chúng tôi có thể triển khai một Adapter
lớp cơ sở đã quan tâm đến việc xử lý các SortedList
mô hình ràng buộc cũng như các ViewHolder
trường hợp và cung cấp một cách thuận tiện để thực hiện Adapter
dựa trên một SortedList
. Vì vậy, chúng ta phải làm hai việc:
- Chúng ta cần tạo một
ViewModel
giao diện mà tất cả các lớp mô hình phải thực hiện
- Chúng ta cần tạo một
ViewHolder
lớp con xác định một bind()
phương thức Adapter
có thể sử dụng để liên kết các mô hình 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ị trong đó RecyclerView
bằng cách chỉ thực hiện các mô hình và có các ViewHolder
triển khai tương ứng . Sử dụng lớp cơ sở này, chúng tôi không phải lo lắng về các chi tiết phức tạp của Adapter
và SortedList
.
Sắp xếp danh sách
Do giới hạn ký tự cho câu trả lời trên StackOverflow, tôi không thể thực hiện 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 đơn giản, tôi đã xuất bản một thư viện trên jCenter 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ụ 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 SortedListAd CHƯƠNG
Để sử dụng, SortedListAdapter
chúng tôi phải thực hiện hai thay đổi:
Thay đổi ViewHolder
để nó mở rộng SortedListAdapter.ViewHolder
. Tham số loại phải là mô hình nên đượ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 bạn 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 thực hiện ViewModel
giao diện:
public class ExampleModel implements SortedListAdapter.ViewModel {
...
}
Sau đó, chúng tôi chỉ cần cập nhật ExampleAdapter
để mở rộng SortedListAdapter
và xóa mọi thứ chúng tôi không cần nữa. Tham số loại phải là loại 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ố loại 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 đó là xong! Tuy nhiên có một điều cuối cùng để đề cập đến: Các SortedListAdapter
không có cùng add()
, remove()
hoặc replaceAll()
phương pháp ban đầu của chúng tôi ExampleAdapter
đã có. 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 trong Editor
trường hợp này và sau 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!