วิธีการกรอง RecyclerView ด้วย SearchView


319

ฉันพยายามที่จะใช้งานSearchViewจากห้องสมุดการสนับสนุน ฉันต้องการให้ผู้ใช้จะใช้SearchViewในการกรองของภาพยนตร์ในListRecyclerView

ฉันได้ติดตามบทเรียนไม่กี่ครั้งและฉันได้เพิ่มSearchViewไปยังActionBarแต่ฉันไม่แน่ใจจริงๆว่าจะไปจากที่นี่ ฉันเคยเห็นตัวอย่างเล็ก ๆ น้อย ๆ แต่ไม่มีพวกเขาใดที่แสดงผลลัพธ์เมื่อคุณเริ่มพิมพ์

นี่คือของฉันMainActivity:

public class MainActivity extends ActionBarActivity {

    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    RecyclerView.Adapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new CardAdapter() {
            @Override
            public Filter getFilter() {
                return null;
            }
        };
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

และนี่คือของฉันAdapter:

public abstract class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> implements Filterable {

    List<Movie> mItems;

    public CardAdapter() {
        super();
        mItems = new ArrayList<Movie>();
        Movie movie = new Movie();
        movie.setName("Spiderman");
        movie.setRating("92");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Doom 3");
        movie.setRating("91");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers");
        movie.setRating("88");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 2");
        movie.setRating("87");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 3");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Noah");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 2");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 3");
        movie.setRating("86");
        mItems.add(movie);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        Movie movie = mItems.get(i);
        viewHolder.tvMovie.setText(movie.getName());
        viewHolder.tvMovieRating.setText(movie.getRating());
    }

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

    class ViewHolder extends RecyclerView.ViewHolder{

        public TextView tvMovie;
        public TextView tvMovieRating;

        public ViewHolder(View itemView) {
            super(itemView);
            tvMovie = (TextView)itemView.findViewById(R.id.movieName);
            tvMovieRating = (TextView)itemView.findViewById(R.id.movieRating);
        }
    }
}

คำตอบ:


913

บทนำ

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

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

ไม่ว่าในกรณีใดผลลัพธ์ควรมีลักษณะดังนี้:

ภาพตัวอย่าง

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

รับบน Google Play

อย่างไรก็ตามเริ่มกันเลย


การตั้งค่า SearchView

ในโฟลเดอร์สร้างไฟล์ใหม่ที่เรียกว่าres/menu main_menu.xmlมันเพิ่มรายการและการตั้งค่าไปactionViewClass android.support.v7.widget.SearchViewเนื่องจากคุณกำลังใช้ไลบรารีการสนับสนุนคุณต้องใช้เนมสเปซของไลบรารีการสนับสนุนเพื่อตั้งค่าactionViewClassแอตทริบิวต์ ไฟล์ xml ของคุณควรมีลักษณะดังนี้:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          app:actionViewClass="android.support.v7.widget.SearchView"
          app:showAsAction="always"/>

</menu>

ในของคุณFragmentหรือActivityคุณต้องขยาย xml เมนูนี้ตามปกติจากนั้นคุณสามารถมองหาMenuItemที่มีSearchViewและใช้สิ่งOnQueryTextListenerที่เราจะใช้เพื่อฟังการเปลี่ยนแปลงข้อความที่ป้อนลงในSearchView:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);

    final MenuItem searchItem = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) searchItem.getActionView();
    searchView.setOnQueryTextListener(this);

    return true;
}

@Override
public boolean onQueryTextChange(String query) {
    // Here is where we are going to implement the filter logic
    return false;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

และตอนนี้SearchViewพร้อมที่จะใช้ เราจะใช้ตรรกะตัวกรองในภายหลังonQueryTextChange()เมื่อเราดำเนินการAdapterตาม


การตั้งค่า Adapter

ก่อนอื่นนี่คือคลาสของโมเดลที่ฉันจะใช้สำหรับตัวอย่างนี้:

public class ExampleModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }
}

RecyclerViewมันเป็นเพียงรูปแบบพื้นฐานของคุณที่จะแสดงข้อความในการ นี่คือเลย์เอาต์ที่ฉันจะใช้เพื่อแสดงข้อความ:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="model"
            type="com.github.wrdlbrnft.searchablerecyclerviewdemo.ui.models.ExampleModel"/>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:clickable="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{model.text}"/>

    </FrameLayout>

</layout>

อย่างที่คุณเห็นฉันใช้ 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()แล้วจะไม่มีการเปลี่ยนแปลงใด ๆ เกิดขึ้น!


4
@TiagoOliveira เอาล่ะทำออกมาได้ดี: D data binding นั้นเป็นสิ่งที่คนที่ไม่คุ้นเคย แต่ฉันรวมมันไว้ด้วยเพราะมันวิเศษมากและฉันต้องการโปรโมตมัน ด้วยเหตุผลบางอย่างที่หลายคนดูเหมือนจะไม่ทราบเกี่ยวกับมัน ...
Xaver Kapeller

78
ฉันยังไม่ได้อ่านคำตอบทั้งหมดฉันต้องหยุดการอ่านสักครึ่งเพื่อเขียนความคิดเห็น - นี่เป็นหนึ่งในคำตอบที่ดีที่สุดที่ฉันพบที่นี่ใน SO! ขอบคุณ!
daneejela

16
ฉันชอบวิธีที่คุณชอบ: "มันไม่ชัดเจนจากคำถามของคุณว่าคุณกำลังมีปัญหาอะไรดังนั้นนี่เป็นตัวอย่างที่สมบูรณ์แบบที่ฉันเพิ่งทำ": D
Fred

7
+1 เพื่อแสดงให้เราเห็นว่า Data Binding นั้นมีอยู่ใน Android! ฉันไม่เคยได้ยินเกี่ยวกับสิ่งนั้นและดูเหมือนว่าฉันจะเริ่มใช้มัน ขอบคุณ
Jorge Casariego

6
วิธีนี้มีความยาวขันและพูดเกินจริงวิศวกรรม ไปสำหรับคนที่สอง
Enrico Casini

194

สิ่งที่คุณต้องทำคือการเพิ่มfilterวิธีการใน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();
}

itemsCopyitemsCopy.addAll(items)จะเริ่มต้นได้ในตัวสร้างอะแดปเตอร์เช่น

หากคุณทำเช่นนั้นเพียงโทรfilterจาก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;
    }
});

เป็นตัวอย่างจากการกรองสมุดโทรศัพท์ของฉันตามชื่อและหมายเลขโทรศัพท์


11
ฉันคิดว่านี่ควรเป็นคำตอบที่ยอมรับได้ มันง่ายขึ้นและใช้งานได้
Jose_GD

6
ง่ายและมีประสิทธิภาพ!
AlxDroidDev

11
โปรดทราบว่าคุณสูญเสียแอนิเมชันหากคุณทำตามวิธีการนี้แทนคำตอบ @Xaver Kapeller
อับอาย

23
ไม่ลองคำตอบที่ยอมรับเพราะยาวเกินไป คำตอบนี้ใช้ได้และใช้งานง่าย อย่าลืมที่จะเพิ่ม "app: actionViewClass =" android.support.v7.widget.SearchView "ในรายการเมนู XML ของคุณ
SajithK

3
ไอเท็มและไอเท็มคัดลอกตรงนี้คืออะไร
Lucky_girl

82

การติดตาม @Shruthi Kamoji ด้วยวิธีที่สะอาดกว่านี้เราสามารถใช้ตัวกรองซึ่งมีความหมายสำหรับ:

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

E ที่นี่เป็น Generic Type คุณสามารถขยายโดยใช้คลาสของคุณ:

public class customerAdapter extends GenericRecycleAdapter<CustomerModel>

หรือเพียงแค่เปลี่ยน E เป็นประเภทที่คุณต้องการ ( <CustomerModel>เช่น)

จาก searchView (วิดเจ็ตที่คุณสามารถวางบน 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;
    }
});

ฉันใช้สิ่งนี้! ทำงานตัวอย่างที่ดีและทั่วไป!
Mateus

สวัสดีใครสามารถช่วยฉัน step-y-step กับสิ่งนี้: stackoverflow.com/questions/40754174/ …
Thorvald Olavsen

คำตอบที่สะอาดที่สุด!
adalpari

4
นี่เป็นคำตอบที่ดีกว่าโหวตมากเพราะการดำเนินการเสร็จสิ้นบนเธรดผู้ปฏิบัติงานในเมธอด performFiltering
อืม

1
แต่คุณกำหนดการอ้างอิงไปยัง List เดียวกันให้กับตัวแปรต่างๆ ตัวอย่างเช่น this.originalList = list; คุณควรใช้ addAll แทนหรือส่งรายการในตัวสร้าง ArrayList
Florian Walther

5

เพียงแค่สร้างรายการสองอะแดปเตอร์หนึ่ง orignal และเป็นหนึ่งชั่วคราวและดำเนินการกรอง

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

ที่ไหน

public GenericBaseAdapter(Context mContext, List<T> itemList) {
        this.mContext = mContext;
        this.itemList = itemList;
        this.origList = itemList;
    }

ทางออกที่ดี ฉันสร้างสองรายการและใช้วิธีการกรองแบบง่าย ฉันดูเหมือนจะไม่ผ่านตำแหน่งอะแดปเตอร์ที่ถูกต้องสำหรับรายการไปยังกิจกรรมถัดไป ฉันขอขอบคุณความคิดหรือความคิดใด ๆ ที่คุณสามารถแนะนำสำหรับสิ่งนี้: stackoverflow.com/questions/46027110/ …
AJW

4

ในอะแดปเตอร์:

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

ในกิจกรรม:

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

ด้วยAndroid ส่วนประกอบสถาปัตยกรรมผ่านการใช้LiveDataนี้สามารถดำเนินการได้อย่างง่ายดายด้วยประเภทของอะแดปเตอร์ คุณเพียงแค่ทำตามขั้นตอนต่อไปนี้:

1.ตั้งค่าข้อมูลของคุณเพื่อกลับจากฐานข้อมูลห้อง เป็นLiveDataดังตัวอย่างด้านล่าง:

@Dao
public interface CustomDAO{

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

2.สร้างวัตถุViewModelเพื่ออัปเดตข้อมูลของคุณแบบสดผ่านวิธีที่จะเชื่อมต่อDAOและUIของคุณ

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.โทรหาข้อมูลของคุณจากViewModel ได้ทันทีโดยผ่านการสืบค้นผ่าน onQueryTextListener ดังนี้:

ภายในonCreateOptionsMenuตั้งฟังของคุณดังนี้

searchView.setOnQueryTextListener(onQueryTextListener);

ตั้งค่าฟังข้อความค้นหาของคุณที่ใดที่หนึ่งในคลาส SearchActivity ดังนี้

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

หมายเหตุ : ขั้นตอน (1. ) และ (2. ) เป็นการนำAAC ViewModelและDAOมาตรฐานมาใช้เท่านั้น "เวทมนต์" จริงที่เกิดขึ้นที่นี่คือในOnQueryTextListenerซึ่งจะอัปเดตผลลัพธ์ของรายการของคุณแบบไดนามิกเมื่อมีการเปลี่ยนแปลงข้อความค้นหา

หากคุณต้องการคำชี้แจงเพิ่มเติมเกี่ยวกับเรื่องนี้โปรดอย่าลังเลที่จะถาม ฉันหวังว่านี่จะช่วยได้ :)


1

นี่คือคำตอบที่ฉันใช้ในการขยาย @klimat เพื่อไม่ให้สูญเสียแอนิเมชันในการกรอง

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

โดยทั่วไปสิ่งที่ทำคือการดูรายการที่สมบูรณ์และเพิ่ม / ลบรายการไปยังรายการที่ถูกกรองทีละรายการ


0

ฉันขอแนะนำให้แก้ไขโซลูชันของ @Xaver Kapeller ด้วย 2 สิ่งด้านล่างเพื่อหลีกเลี่ยงปัญหาหลังจากที่คุณล้างข้อความค้นหา (ตัวกรองไม่ทำงานอีกต่อไป) เนื่องจากรายการด้านหลังของอะแดปเตอร์มีขนาดเล็กกว่ารายการตัวกรองและดัชนี IndexOutOfBoundsException ดังนั้นรหัสจำเป็นต้องแก้ไขดังต่อไปนี้

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

และปรับเปลี่ยนได้ในฟังก์ชั่น 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); 
    }
}

หวังว่ามันจะช่วยคุณได้!


นั่นไม่จำเป็นเลย
Xaver Kapeller

สำหรับคำตอบดั้งเดิมหากคุณไม่ทำเช่นนั้น IndexOutOfBoundsException จะเกิดขึ้นดังนั้นทำไมจึงไม่จำเป็น ???? คุณต้องการบันทึกหรือไม่? @XaverKapeller
toidv

จะไม่มีข้อยกเว้นเกิดขึ้นหากคุณใช้งานในAdapterทางที่ผิด Adapterโดยไม่เห็นรหัสของคุณผมคิดว่าปัญหาที่มีแนวโน้มมากที่สุดคือการที่คุณไม่ได้ส่งสำเนาของรายการกับรายการทั้งหมดไปยัง
Xaver Kapeller

บันทึกข้อผิดพลาด: W / System.err: java.lang.IndexOutOfBoundsException: ดัชนีไม่ถูกต้อง 36 ขนาดคือ 35 W / System.err: ที่ java.util.ArrayList.throwIndexOutOfBoundsException (ArrayList.java:255) W / System.err: ที่ java.util.ArrayList.add (ArrayList.java:147) W / System.err: ที่ com.quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.addItem (MultipleSelectFilterAdapter.java:125) W / System.err: ที่ com .quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.applyAndAnimateAdditions (MultipleSelectFilterAdapter.java:78)
toidv

โปรดช่วยตรวจสอบซอร์สโค้ดด้านล่าง @XaverKapeller gist.github.com/toidv/fe71dc45169e4138271b52fdb29420c5
toidv

0

Recyclerview พร้อม searchview และ clicklistener

เพิ่มส่วนต่อประสานในอะแดปเตอร์ของคุณ

public interface SelectedUser{

    void selectedUser(UserModel userModel);

}

ใช้อินเตอร์เฟสใน mainactivity ของคุณและแทนที่วิธีการ @Override โมฆะสาธารณะที่เลือกไว้ผู้ใช้ (UserModel userModel) {

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



}

บทแนะนำแบบเต็มและซอร์สโค้ด: Recyclerview พร้อม searchview และ onclicklistener


-1

ฉันได้แก้ไขปัญหาเดียวกันโดยใช้ลิงก์ที่มีการแก้ไขบางอย่างในนั้น ค้นหาตัวกรองบน ​​RecyclerView ด้วยการ์ด เป็นไปได้ไหม (หวังว่านี่จะช่วยได้)

นี่คือคลาสอะแดปเตอร์ของฉัน

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

// คลาสตัวกรอง

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

}

// คลาสของกิจกรรม

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

ในวิธี OnQueryTextChangeListener () ใช้อะแดปเตอร์ของคุณ ฉันได้โยนมันเป็นชิ้น ๆ เพราะ adpter ของฉันอยู่ในส่วน คุณสามารถใช้อะแด็ปเตอร์โดยตรงหากอยู่ในคลาสกิจกรรมของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.