บทนำ
เนื่องจากคำถามของคุณไม่ชัดเจนว่าคุณกำลังมีปัญหาอะไรฉันจึงเขียนคำแนะนำสั้น ๆ เกี่ยวกับวิธีใช้คุณลักษณะนี้ หากคุณยังมีคำถามอย่าลังเลที่จะถาม
ฉันมีตัวอย่างการทำงานของทุกสิ่งที่ฉันกำลังพูดถึงที่นี่ในที่เก็บ GitHubนี้
หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับโครงการตัวอย่างแวะไปที่หน้าแรกของโครงการ
ไม่ว่าในกรณีใดผลลัพธ์ควรมีลักษณะดังนี้:

หากคุณต้องการเล่นแอพตัวอย่างคุณสามารถติดตั้งได้จาก Play Store:

อย่างไรก็ตามเริ่มกันเลย
การตั้งค่า 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>
อย่างที่คุณเห็นฉันใช้ Data Binding หากคุณไม่เคยทำงานกับการผูกข้อมูลมาก่อนอย่าท้อถอย! มันง่ายและมีประสิทธิภาพมาก แต่ฉันไม่สามารถอธิบายได้ว่ามันทำงานอย่างไรในขอบเขตของคำตอบนี้
นี่คือ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);
}
}
ไม่มีอะไรพิเศษอีกแล้ว มันใช้การผูกข้อมูลเพื่อผูกคลาสโมเดลกับเลย์เอาต์นี้ตามที่เราได้กำหนดไว้ใน layout xml ด้านบน
ในที่สุดเราก็มาถึงส่วนที่น่าสนใจอย่างยิ่ง: การเขียนอะแดปเตอร์ ฉันจะข้ามไปใช้ขั้นพื้นฐานของAdapterและแทนที่จะไปมุ่งเน้นที่ส่วนที่เกี่ยวข้องกับคำตอบนี้
แต่ก่อนอื่นมีสิ่งหนึ่งที่เราต้องพูดถึงคือ: SortedListชั้นเรียน
SortedList
SortedListเป็นเครื่องมือที่น่าตื่นตาตื่นใจอย่างสมบูรณ์ซึ่งเป็นส่วนหนึ่งของRecyclerViewห้องสมุด มันจะคอยดูแลการแจ้งเตือนAdapterเกี่ยวกับการเปลี่ยนแปลงของชุดข้อมูลและทำให้มันมีประสิทธิภาพ สิ่งเดียวที่คุณต้องทำคือระบุลำดับขององค์ประกอบ คุณจำเป็นต้องทำอย่างนั้นโดยการใช้compare()วิธีการที่เปรียบเทียบสององค์ประกอบในเช่นเดียวกับ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ฯลฯ Adapterคุณมีการโทรเทียบเท่าแจ้งวิธีการของคุณ สามวิธีที่ด้านล่างcompare, areContentsTheSameและareItemsTheSameคุณจะต้องดำเนินการตามสิ่งที่ชนิดของวัตถุที่คุณต้องการแสดงและในสิ่งที่สั่งซื้อวัตถุเหล่านี้ควรจะปรากฏขึ้นบนหน้าจอ
มาดูวิธีการเหล่านี้ทีละคน:
@Override
public int compare(ExampleModel a, ExampleModel b) {
return mComparator.compare(a, b);
}
นี่เป็นcompare()วิธีที่ฉันพูดถึงก่อนหน้านี้ ในตัวอย่างนี้ฉันเพิ่งผ่านการโทรไปยังComparatorซึ่งเปรียบเทียบทั้งสองรุ่น หากคุณต้องการให้รายการปรากฏตามลำดับตัวอักษรบนหน้าจอ ตัวเปรียบเทียบนี้อาจมีลักษณะเช่นนี้:
private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
@Override
public int compare(ExampleModel a, ExampleModel b) {
return a.getText().compareTo(b.getText());
}
};
ตอนนี้ลองมาดูวิธีถัดไป:
@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
return oldItem.equals(newItem);
}
วัตถุประสงค์ของวิธีนี้คือการตรวจสอบว่าเนื้อหาของแบบจำลองมีการเปลี่ยนแปลงหรือไม่ SortedListนี้ใช้เพื่อตรวจสอบว่าเหตุการณ์การเปลี่ยนแปลงจะต้องเรียก - ในคำอื่น ๆ ถ้าRecyclerViewควร Crossfade รุ่นเก่าและใหม่ หากคุณจำลองคลาสมีความถูกต้องequals()และhashCode()การนำไปใช้งานคุณสามารถนำไปใช้งานได้ตามปกติ ถ้าเราเพิ่มequals()และhashCode()นำไปใช้กับExampleModelคลาสมันควรมีลักษณะดังนี้:
public class ExampleModel implements SortedListAdapter.ViewModel {
private final long mId;
private final String mText;
public ExampleModel(long id, String text) {
mId = id;
mText = text;
}
public long getId() {
return mId;
}
public String getText() {
return mText;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ExampleModel model = (ExampleModel) o;
if (mId != model.mId) return false;
return mText != null ? mText.equals(model.mText) : model.mText == null;
}
@Override
public int hashCode() {
int result = (int) (mId ^ (mId >>> 32));
result = 31 * result + (mText != null ? mText.hashCode() : 0);
return result;
}
}
หมายเหตุด้านด่วน: IDE ส่วนใหญ่เช่น Android Studio, IntelliJ และ Eclipse มีฟังก์ชั่นในการสร้างequals()และhashCode()การนำไปใช้งานสำหรับคุณเพียงแค่กดปุ่ม! ดังนั้นคุณไม่จำเป็นต้องใช้มันด้วยตนเอง ค้นหาบนอินเทอร์เน็ตว่ามันทำงานอย่างไรใน IDE ของคุณ!
ตอนนี้ลองมาดูวิธีสุดท้าย:
@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
return item1.getId() == item2.getId();
}
SortedListใช้วิธีนี้เพื่อตรวจสอบว่าสองรายการอ้างถึงสิ่งเดียวกัน ในแง่ที่ง่ายที่สุด (โดยไม่อธิบายวิธีการSortedListทำงาน) สิ่งนี้ถูกใช้เพื่อกำหนดว่าวัตถุนั้นมีอยู่แล้วในListและถ้าต้องการเพิ่มย้ายหรือเปลี่ยนภาพเคลื่อนไหวจำเป็นต้องเล่น หากรุ่นของคุณมีรหัสคุณมักจะเปรียบเทียบเพียงแค่รหัสในวิธีนี้ หากคุณไม่จำเป็นต้องหาวิธีอื่นในการตรวจสอบสิ่งนี้ แต่คุณจะต้องดำเนินการตามนี้โดยขึ้นอยู่กับแอพเฉพาะของคุณ โดยปกติแล้วมันเป็นตัวเลือกที่ง่ายที่สุดในการให้ id ทุกรุ่นซึ่งอาจเป็นฟิลด์คีย์หลักหากคุณทำการสืบค้นข้อมูลจากฐานข้อมูล
ด้วยการSortedList.Callbackใช้งานอย่างถูกต้องเราสามารถสร้างตัวอย่างของSortedList:
final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);
ในฐานะที่เป็นพารามิเตอร์แรกในตัวสร้างของSortedListคุณจะต้องผ่านชั้นเรียนของแบบจำลองของคุณ พารามิเตอร์อื่น ๆ เป็นเพียงที่SortedList.Callbackเรากำหนดไว้ข้างต้น
ทีนี้มาลงมือทำธุรกิจกันเถอะ: ถ้าเราใช้Adaptera ด้วย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();
}
เราไม่จำเป็นต้องโทรวิธีแจ้งเตือนใด ๆ ที่นี่เพราะสิ่งSortedListนี้ได้ดำเนินการผ่านSortedList.Callback! นอกเหนือจากนั้นการใช้วิธีการเหล่านี้ค่อนข้างตรงไปตรงมาด้วยข้อยกเว้น: วิธีการลบที่เอาListรูปแบบของ เนื่องจากSortedListมีวิธีการลบเพียงวิธีเดียวซึ่งสามารถลบวัตถุเดียวเราจึงจำเป็นต้องวนซ้ำในรายการและลบแบบจำลองทีละคน การเรียกbeginBatchedUpdates()ที่จุดเริ่มต้นเป็นชุดของการเปลี่ยนแปลงทั้งหมดที่เราจะทำSortedListร่วมกันและปรับปรุงประสิทธิภาพ เมื่อเราเรียกมันendBatchedUpdates()ว่าRecyclerViewจะได้รับแจ้งเกี่ยวกับการเปลี่ยนแปลงทั้งหมดในครั้งเดียว
นอกจากนี้สิ่งที่คุณต้องเข้าใจก็คือถ้าคุณเพิ่มวัตถุลงในSortedListและวัตถุนั้นอยู่ในSortedListนั้นแล้วจะไม่ถูกเพิ่มอีก แทนการSortedListใช้areContentsTheSame()วิธีการที่จะคิดออกว่าวัตถุมีการเปลี่ยนแปลง - และถ้ามันมีรายการใน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();
}
วิธีนี้เป็นชุดการอัพเดททั้งหมดพร้อมกันอีกครั้งเพื่อเพิ่มประสิทธิภาพ การวนซ้ำแรกอยู่ในสิ่งที่ตรงกันข้ามเนื่องจากการลบรายการเมื่อเริ่มต้นจะทำให้ดัชนีของรายการทั้งหมดที่เกิดขึ้นหลังจากนั้นยุ่งเหยิงและสิ่งนี้อาจนำไปสู่ปัญหาในบางกรณีเช่นความไม่สอดคล้องกันของข้อมูล หลังจากนั้นเราก็เพิ่มListไปSortedListใช้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โมเดลที่เป็นไปได้ทั้งหมดก่อน สำหรับตัวอย่างนี้ฉันสร้างหนึ่งListในExampleModelอินสแตนซ์จากอาร์เรย์ของภาพยนตร์:
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()และส่งผ่านค่าListของExampleModels รวมถึงสตริงการสืบค้น จากนั้นเราจะเรียก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เพื่อที่เราจะต้องทำสองสิ่ง:
- เราจำเป็นต้องสร้าง
ViewModelอินเตอร์เฟสที่คลาสโมเดลทั้งหมดต้องนำไปใช้
- เราจำเป็นต้องสร้าง
ViewHolderคลาสย่อยซึ่งกำหนดbind()วิธีการที่Adapterสามารถใช้ในการผูกโมเดลโดยอัตโนมัติ
สิ่งนี้ช่วยให้เราสามารถมุ่งเน้นไปที่เนื้อหาที่ควรจะแสดงในRecyclerViewโดยการใช้โมเดลและมีViewHolderการใช้งานที่สอดคล้องกัน การใช้ฐานชั้นนี้เราไม่ต้องกังวลเกี่ยวกับรายละเอียดซับซ้อนของและAdapterSortedList
SortedListAdapter
เนื่องจากขีด จำกัด อักขระสำหรับคำตอบใน StackOverflow ฉันไม่สามารถผ่านแต่ละขั้นตอนของการใช้คลาสพื้นฐานนี้หรือแม้แต่เพิ่มซอร์สโค้ดแบบเต็มที่นี่ แต่คุณสามารถค้นหาซอร์สโค้ดแบบเต็มของคลาสพื้นฐานนี้ - ฉันเรียกมันว่าSortedListAdapter- ในนี้GitHub สรุปสาระสำคัญ
เพื่อให้ชีวิตของคุณง่ายขึ้นฉันได้เผยแพร่ห้องสมุดบน jCenter ซึ่งมีSortedListAdapter! หากคุณต้องการใช้งานสิ่งที่คุณต้องทำคือเพิ่มการพึ่งพานี้ไปยังไฟล์ build.gradle ของแอปของคุณ:
compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'
คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับห้องสมุดนี้บนหน้าแรกของห้องสมุด
ใช้ SortedListAdapter
ในการใช้งานSortedListAdapterเราต้องทำการเปลี่ยนแปลงสองอย่าง:
เปลี่ยนเพื่อให้มันยาวยืดViewHolder SortedListAdapter.ViewHolderพารามิเตอร์ type ควรเป็นรุ่นที่ควรผูกกับสิ่งนี้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นี้ 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()แล้วจะไม่มีการเปลี่ยนแปลงใด ๆ เกิดขึ้น!