บทนำ
เนื่องจากคำถามของคุณไม่ชัดเจนว่าคุณกำลังมีปัญหาอะไรฉันจึงเขียนคำแนะนำสั้น ๆ เกี่ยวกับวิธีใช้คุณลักษณะนี้ หากคุณยังมีคำถามอย่าลังเลที่จะถาม
ฉันมีตัวอย่างการทำงานของทุกสิ่งที่ฉันกำลังพูดถึงที่นี่ในที่เก็บ 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
เรากำหนดไว้ข้างต้น
ทีนี้มาลงมือทำธุรกิจกันเถอะ: ถ้าเราใช้Adapter
a ด้วย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
ของExampleModel
s รวมถึงสตริงการสืบค้น จากนั้นเราจะเรียก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
การใช้งานที่สอดคล้องกัน การใช้ฐานชั้นนี้เราไม่ต้องกังวลเกี่ยวกับรายละเอียดซับซ้อนของและAdapter
SortedList
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()
แล้วจะไม่มีการเปลี่ยนแปลงใด ๆ เกิดขึ้น!