Jak filtrować RecyclerView za pomocą SearchView


319

Próbuję zaimplementować SearchViewz biblioteki wsparcia. Chcę, aby użytkownik używał SearchViewdo filtrowania Listfilmów w RecyclerView.

Do tej pory zapoznałem się z kilkoma samouczkami i dodałem je SearchViewdo ActionBar, ale tak naprawdę nie jestem pewien, dokąd mam się udać. Widziałem kilka przykładów, ale żaden z nich nie pokazuje wyników podczas pisania.

To jest moje 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);
    }
}

A to moje 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);
        }
    }
}

Odpowiedzi:


913

Wprowadzenie

Ponieważ z twojego pytania nie jest tak naprawdę jasne, z czym dokładnie masz problemy, napisałem ten krótki przewodnik na temat implementacji tej funkcji; jeśli nadal masz pytania, możesz je zadać.

Mam działający przykład wszystkiego, o czym mówię tutaj w tym repozytorium GitHub .
Jeśli chcesz dowiedzieć się więcej o przykładowym projekcie, odwiedź stronę główną projektu .

W każdym razie wynik powinien wyglądać mniej więcej tak:

obraz demonstracyjny

Jeśli najpierw chcesz się pobawić aplikacją demonstracyjną, możesz zainstalować ją ze Sklepu Play:

Pobierz w Google Play

W każdym razie zacznijmy.


Konfigurowanie SearchView

W folderze res/menuutwórz nowy plik o nazwie main_menu.xml. W niej dodać element i ustawić actionViewClasssię android.support.v7.widget.SearchView. Ponieważ korzystasz z biblioteki wsparcia, musisz użyć przestrzeni nazw biblioteki wsparcia, aby ustawić actionViewClassatrybut. Twój plik xml powinien wyglądać mniej więcej tak:

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

W swoim Fragmentlub Activitymusisz napompować to menu xml jak zwykle, możesz poszukać, MenuItemktóry zawiera SearchViewi zaimplementować, OnQueryTextListenerktórego będziemy używać, aby nasłuchiwać zmian w tekście wprowadzonym do 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;
}

A teraz SearchViewjest gotowy do użycia. Zaimplementujemy logikę filtrowania później, onQueryTextChange()gdy skończymy wdrażanie Adapter.


Konfigurowanie Adapter

Przede wszystkim jest to klasa modelu, której zamierzam użyć w tym przykładzie:

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

To tylko podstawowy model, który wyświetla tekst w RecyclerView. Oto układ, którego zamierzam użyć do wyświetlenia tekstu:

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

Jak widać, używam powiązania danych. Jeśli nigdy wcześniej nie pracowałeś z powiązaniem danych, nie zniechęcaj się! Jest to bardzo proste i wydajne, ale nie mogę wyjaśnić, jak to działa w zakresie tej odpowiedzi.

To jest ViewHolderdla ExampleModelklasy:

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

Znowu nic specjalnego. Po prostu używa powiązania danych, aby powiązać klasę modelu z tym układem, jak zdefiniowaliśmy w powyższym pliku xml układu.

Teraz możemy wreszcie przejść do naprawdę interesującej części: pisania adaptera. Pominę podstawową implementację Adapteri zamiast tego skoncentruję się na częściach, które są istotne dla tej odpowiedzi.

Ale najpierw jest jedna rzecz, o której musimy porozmawiać: SortedListklasa.


SortedList

SortedListJest zupełnie niesamowite narzędzie, które jest częścią RecyclerViewbiblioteki. Dba o powiadamianie Adaptero zmianach w zestawie danych i robi to w bardzo wydajny sposób. Jedyne, co musisz zrobić, to określić kolejność elementów. Musisz to zrobić, implementując compare()metodę, która porównuje dwa elementy w SortedListtaki sam sposób jak Comparator. Ale zamiast sortowania Listsłuży do sortowania elementów w RecyclerView!

W SortedListwspółdziała z Adapterza pośrednictwem Callbackklasy, które trzeba realizować:

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

W sposobach na górze jak zwrotnego onMoved, onInserteditp trzeba nazwać odpowiednikiem zawiadomić sposób dokonania Adapter. Trzy sposoby na dole compare, areContentsTheSamei areItemsTheSametrzeba zaimplementować według jakiego rodzaju obiektów, które mają być wyświetlane iw jakiej kolejności obiekty te powinny pojawić się na ekranie.

Przeanalizujmy kolejno te metody:

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

To jest compare()metoda, o której mówiłem wcześniej. W tym przykładzie właśnie przekazuję połączenie do modelu, Comparatorktóry porównuje oba modele. Jeśli chcesz, aby elementy były wyświetlane na ekranie w kolejności alfabetycznej. Ten komparator może wyglądać następująco:

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

Teraz spójrzmy na następną metodę:

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

Celem tej metody jest ustalenie, czy zawartość modelu uległa zmianie. Do SortedListużywa tego, aby ustalić, czy zdarzenie zmiana musi być wywoływany - innymi słowy, czy RecyclerViewnależy płynne przejście starą i nową wersję. Jeśli modelujesz klasy, masz poprawną equals()i hashCode()implementację, zazwyczaj możesz ją po prostu zaimplementować jak wyżej. Jeśli dodamy do klasy implementację equals()i , powinna ona wyglądać mniej więcej tak: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;
    }
}

Krótka uwaga: większość IDE, takich jak Android Studio, IntelliJ i Eclipse, ma funkcję generowania equals()i hashCode()implementacji dla Ciebie za naciśnięciem jednego przycisku! Więc nie musisz ich wdrażać samodzielnie. Sprawdź w Internecie, jak to działa w twoim IDE!

Teraz spójrzmy na ostatnią metodę:

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

SortedListKorzysta z tej metody, aby sprawdzić, czy dwie pozycje odnoszą się do tego samego. Mówiąc najprościej (bez wyjaśnienia, jak SortedListdziała), służy to do ustalenia, czy obiekt jest już zawarty Listi czy należy odtworzyć animację dodania, przesunięcia lub zmiany. Jeśli twoje modele mają identyfikator, zwykle porównujesz tylko identyfikator w tej metodzie. Jeśli nie, musisz wymyślić inny sposób, aby to sprawdzić, ale w końcu wdrożenie tego zależy od konkretnej aplikacji. Zwykle jest to najprostsza opcja nadania wszystkim modelom identyfikatora - może to być na przykład pole klucza podstawowego, jeśli przeszukujesz dane z bazy danych.

Dzięki SortedList.Callbackpoprawnie zaimplementowanemu możemy utworzyć instancję SortedList:

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

Jako pierwszy parametr w konstruktorze SortedListmusisz przekazać klasę swoich modeli. Drugi parametr to po prostu SortedList.Callbackzdefiniowany powyżej.

Przejdźmy teraz do rzeczy : jeśli zaimplementujemy Adapterz SortedList, powinno to wyglądać mniej więcej tak:

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

ComparatorUżywane do sortowania pozycja jest przekazywana przez konstruktora, dzięki czemu możemy korzystać z tego samego Adapter, nawet jeśli elementy mają być wyświetlane w innej kolejności.

Teraz już prawie gotowe! Ale najpierw potrzebujemy sposobu dodawania lub usuwania elementów do Adapter. W tym celu możemy dodać metody, Adapterktóre pozwalają nam dodawać i usuwać elementy do 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();
}

Nie musimy tutaj wywoływać żadnych metod powiadamiania, ponieważ SortedListjuż to robi za pośrednictwem SortedList.Callback! Poza tym implementacja tych metod jest dość prosta, z jednym wyjątkiem: metodą remove, która usuwa Listmodele. Ponieważ SortedListma tylko jedną metodę remove, która może usunąć pojedynczy obiekt, musimy zapętlić listę i usuwać modele jeden po drugim. Wezwanie beginBatchedUpdates()na początku grupuje wszystkie zmiany, które SortedListrazem wprowadzimy, i poprawia wydajność. Kiedy wzywamy jest powiadamiany o wszelkich zmianach na raz.endBatchedUpdates()RecyclerView

Ponadto musisz zrozumieć, że jeśli dodasz obiekt do obiektu, który SortedListjuż znajduje się w SortedListnim, nie zostanie dodany ponownie. Zamiast tego SortedListużywa tej areContentsTheSame()metody, aby dowiedzieć się, czy obiekt się zmienił - a jeśli ma element w, RecyclerViewzostanie zaktualizowany.

W każdym razie to, co zwykle wolę, to jedna metoda, która pozwala mi na zastąpienie wszystkich przedmiotów RecyclerViewnaraz. Usuń wszystko, czego nie ma w, Listi dodaj wszystkie elementy, których brakuje w 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();
}

Ta metoda ponownie grupuje wszystkie aktualizacje razem, aby zwiększyć wydajność. Pierwsza pętla jest odwrotna, ponieważ usunięcie elementu na początku zepsułoby indeksy wszystkich elementów, które pojawią się po nim, co może w niektórych przypadkach prowadzić do problemów, takich jak niespójności danych. Następnie dodajemy po prostu Listdo za SortedListpomocą, addAll()aby dodać wszystkie elementy, które nie są jeszcze w SortedList- i - jak opisano powyżej - zaktualizować wszystkie elementy, które już są w, SortedListale zostały zmienione.

I z tym Adapterjest kompletny. Całość powinna wyglądać mniej więcej tak:

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

Jedyne, czego teraz brakuje, to wdrożenie filtrowania!


Implementacja logiki filtrowania

Aby zaimplementować logikę filtrowania, musimy najpierw zdefiniować Listwszystkie możliwe modele. W tym przykładzie utworzyć Listz ExampleModelprzypadków z tablicy filmy:

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

Nic specjalnego się tutaj nie dzieje, po prostu tworzymy instancję Adapteri ustawiamy ją na RecyclerView. Następnie tworzymy Listmodele z nazw filmów w MOVIEStablicy. Następnie dodajemy wszystkie modele do SortedList.

Teraz możemy wrócić do onQueryTextChange()zdefiniowanej wcześniej i rozpocząć implementację logiki filtru:

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

To znowu jest całkiem proste. Wywołujemy metodę filter()i przekazać w Listod ExampleModels, a także ciąg kwerendy. Następnie zadzwoń replaceAll()na Adapteri przekazać w filtrowane Listzwrócony przez filter(). Musimy również wezwać scrollToPosition(0)do, RecyclerViewaby upewnić się, że użytkownik zawsze widzi wszystkie elementy podczas wyszukiwania czegoś. W przeciwnym razie RecyclerViewfiltr może pozostać w przewijanej pozycji podczas filtrowania, a następnie ukryć kilka elementów. Przewijanie do góry zapewnia lepszą obsługę podczas wyszukiwania.

Jedyne, co pozostało do zrobienia, to wdrożenie filter()się:

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

Pierwszą rzeczą, którą tutaj robimy, jest wywołanie toLowerCase()ciągu zapytania. Nie chcemy, aby nasza funkcja wyszukiwania rozróżniała toLowerCase()wielkość liter i wywołując wszystkie porównywane ciągi znaków możemy zagwarantować, że zwracamy te same wyniki bez względu na wielkość liter. Następnie iteruje tylko wszystkie modele, Listktóre do niego przeszliśmy i sprawdza, czy ciąg zapytania jest zawarty w tekście modelu. Jeśli tak, to model jest dodawany do filtrowanego List.

I to wszystko! Powyższy kod będzie działał na poziomie API 7 i wyższym, a począwszy od API poziomu 11 otrzymasz animacje przedmiotów za darmo!

Zdaję sobie sprawę, że jest to bardzo szczegółowy opis, który prawdopodobnie sprawia, że ​​cała ta sprawa wydaje się bardziej skomplikowana niż w rzeczywistości, ale istnieje sposób na uogólnienie tego całego problemu i uczynienie implementacji Adapteropartym na SortedListznacznie prostszym.


Uogólnienie problemu i uproszczenie adaptera

W tej sekcji nie będę szczegółowo omawiał - częściowo dlatego, że wyczerpuję się limit znaków dla odpowiedzi na Przepełnienie stosu, ale także dlatego, że większość z nich już wyjaśniono powyżej - ale podsumowując zmiany: Możemy zaimplementować Adapterklasę podstawową który już zajmuje się radzeniem sobie z SortedListmodelami ViewHolderi ich wiązaniem z instancjami i zapewnia wygodny sposób implementacji modelu Adapteropartego na SortedList. W tym celu musimy zrobić dwie rzeczy:

  • Musimy stworzyć ViewModelinterfejs, który wszystkie klasy modeli muszą wdrożyć
  • Musimy utworzyć ViewHolderpodklasę, która definiuje bind()metodę, której Adaptermożna użyć do automatycznego wiązania modeli.

To pozwala nam skupić się na treści, która ma być wyświetlana po RecyclerViewprostu poprzez wdrożenie modeli i odpowiadających im ViewHolderimplementacji. Korzystając z tej klasy bazowej, nie musimy się martwić o skomplikowane szczegóły Adapteri SortedList.

SortedListAdapter

Z powodu limitu znaków w odpowiedziach na StackOverflow nie mogę przejść przez każdy etap implementacji tej klasy bazowej ani nawet dodać tutaj pełnego kodu źródłowego, ale pełny kod źródłowy tej klasy bazowej - nazwałem to SortedListAdapter- w tym GitHub Gist .

Aby ułatwić Ci życie, opublikowałem bibliotekę w jCenter, która zawiera SortedListAdapter! Jeśli chcesz go użyć, wystarczy dodać tę zależność do pliku build.gradle aplikacji:

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

Więcej informacji o tej bibliotece można znaleźć na stronie głównej biblioteki .

Korzystanie z SortedListAdapter

Aby użyć, SortedListAdaptermusimy wprowadzić dwie zmiany:

  • Zmień ViewHoldertak, aby się wydłużył SortedListAdapter.ViewHolder. Parametrem typu powinien być model, który powinien być z tym związany ViewHolder- w tym przypadku ExampleModel. Trzeba powiązać dane do modeli performBind()zamiast 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);
        }
    }
    
  • Upewnij się, że wszystkie modele implementują ViewModelinterfejs:

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

Następnie musimy tylko zaktualizować, ExampleAdapteraby rozszerzyć SortedListAdapteri usunąć wszystko, czego już nie potrzebujemy. Parametr typu powinien być typem modelu, z którym pracujesz - w tym przypadku ExampleModel. Ale jeśli pracujesz z różnymi typami modeli, ustaw parametr type na 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);
    }
}

Potem skończyliśmy! Jednak jedna rzecz wspomnieć: Obiekt SortedListAdapternie posiada te same add(), remove()lub replaceAll()metod nasz oryginalny ExampleAdaptermieliśmy. Używa osobnego Editorobiektu do modyfikowania pozycji na liście, do których można uzyskać dostęp za pomocą edit()metody. Jeśli więc chcesz usunąć lub dodać elementy, do których musisz zadzwonić, edit()dodaj i usuń elementy w tym Editorwystąpieniu, a gdy skończysz, zadzwoń commit(), aby zastosować zmiany do SortedList:

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

Wszystkie zmiany wprowadzane w ten sposób są grupowane w celu zwiększenia wydajności. replaceAll()Metoda wdrożyliśmy w powyższych rozdziałach jest również obecny na tym Editorobiekcie:

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

Jeśli zapomnisz zadzwonić, commit()żadna ze zmian nie zostanie zastosowana!


4
@TiagoOliveira Cóż, to po prostu działa od razu po wyjęciu z pudełka: D Powiązanie danych jest przeszkodą dla osób, które go nie znają, ale i tak je załączyłem, ponieważ jest niesamowite i chcę je promować. Z jakiegoś powodu niewiele osób zdaje się o tym wiedzieć ...
Xaver Kapeller,

78
Jeszcze nie przeczytałem całej odpowiedzi, musiałem przerwać czytanie gdzieś na pół, aby napisać ten komentarz - jest to jedna z najlepszych odpowiedzi, jakie znalazłem tutaj na SO! Dzięki!
daneejela

16
Uwielbiam to, jak się masz: „Z twojego pytania nie wynika jasno, z czym masz problem, więc oto pełny przykład, który właśnie zrobiłem”: D
Fred

7
+1 tylko po to, aby pokazać nam, że w systemie Android istnieje powiązanie danych! Nigdy o tym nie słyszałem i wydaje mi się, że zacznę go używać. Dzięki
Jorge Casariego,

6
To rozwiązanie jest absurdalnie długie i ogólnie mówiąc nadmiernie zaprojektowane. Idź po drugi.
Enrico Casini

194

Wszystko, co musisz zrobić, to dodać filtermetodę w 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();
}

itemsCopyjest inicjowany w konstruktorze adaptera jak itemsCopy.addAll(items).

Jeśli to zrobisz, po prostu zadzwoń filterz 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;
    }
});

To przykład filtrowania mojej książki telefonicznej według nazwy i numeru telefonu.


11
Myślę, że to powinna być zaakceptowana odpowiedź. To jest prostsze i po prostu działa
Jose_GD

6
Prosty i wydajny!
AlxDroidDev

11
Pamiętaj, że utracisz animację, jeśli zastosujesz to podejście zamiast odpowiedzi @Xaver Kapeller.
zdziwiony

23
Nie próbowałem zaakceptować odpowiedzi, ponieważ jest ona zbyt długa. Ta odpowiedź działa i jest łatwa do wdrożenia. Nie zapomnij dodać „app: actionViewClass =" android.support.v7.widget.SearchView ”do pozycji menu XML.
SajithK

3
Co to są dokładnie przedmioty i przedmioty Skopiuj tutaj?
Lucky_girl

82

Podążając za @Shruthi Kamoji w bardziej przejrzysty sposób, możemy po prostu użyć filtrowalnego, przeznaczonego do tego:

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 tutaj jest typem ogólnym, możesz go rozszerzyć za pomocą swojej klasy:

public class customerAdapter extends GenericRecycleAdapter<CustomerModel>

Lub po prostu zmień E na żądany typ ( <CustomerModel>na przykład)

Następnie z searchView (widget, który możesz umieścić w 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;
    }
});

Używam czegoś takiego! Działa dobrze i ogólna próbka!
Mateus

Witaj, kto może mi pomóc krok po kroku w tym przypadku: stackoverflow.com/questions/40754174/…
Thorvald Olavsen

Najczystsza odpowiedź!
adalpari

4
Jest to o wiele lepsze niż wyżej głosowana odpowiedź, ponieważ operacja jest wykonywana na wątku roboczym w metodzie performFiltering.
Hmmm

1
Ale przypisujesz odwołanie do tej samej listy do różnych zmiennych. Na przykład this.originalList = list; Zamiast tego powinieneś użyć addAll lub przekazać listę w konstruktorze ArrayList
Florian Walther

5

po prostu stwórz dwie listy w adapterze jeden orignal i jeden temp i zaimplementuj filtrowalne .

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

gdzie

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

Niezłe rozwiązanie. Utworzyłem dwie listy i użyłem prostej metody filtrowania. Nie mogę podać prawidłowej pozycji adaptera dla elementu do następnego działania. Byłbym wdzięczny za wszelkie przemyślenia i pomysły na ten temat: stackoverflow.com/questions/46027110/...
AJW

4

W adapterze:

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

W działaniu:

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

Dzięki komponentom architektury Android za pomocą LiveData można to łatwo wdrożyć z dowolnym rodzajem adaptera . Musisz po prostu wykonać następujące czynności:

1. Skonfiguruj swoje dane, aby powróciły z bazy danych pomieszczeń jako LiveData, jak w poniższym przykładzie:

@Dao
public interface CustomDAO{

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

2. Utwórz obiekt ViewModel, aby aktualizować dane na żywo za pomocą metody, która połączy DAO i interfejs użytkownika

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. Wywoływaj swoje dane z ViewModel w locie, przekazując zapytanie przez onQueryTextListener, jak poniżej:

Wewnątrz onCreateOptionsMenuustaw swój słuchacz w następujący sposób

searchView.setOnQueryTextListener(onQueryTextListener);

Ustaw detektor zapytań gdzieś w klasie SearchActivity w następujący sposób

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

Uwaga : Kroki (1.) i (2.) są standardową implementacją AAC ViewModel i DAO , jedyną prawdziwą „magią”, która się tu dzieje, jest OnQueryTextListener, który będzie dynamicznie aktualizował wyniki listy wraz ze zmianą tekstu zapytania.

Jeśli potrzebujesz więcej wyjaśnień w tej sprawie, nie wahaj się zapytać. Mam nadzieję, że to pomogło :).


1

To jest moje zdanie na temat rozszerzenia odpowiedzi @klimat na brak utraty animacji filtrowania.

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

Zasadniczo przegląda pełną listę i dodaje / usuwa elementy jeden po drugim do filtrowanej listy.


0

Polecam zmodyfikować rozwiązanie @Xaver Kapeller z 2 rzeczami poniżej, aby uniknąć problemu po wyczyszczeniu przeszukiwanego tekstu (filtr już nie działał), ponieważ lista tylna adaptera ma mniejszy rozmiar niż lista filtrów i wystąpił wyjątek IndexOutOfBoundsException. Więc kod należy zmodyfikować jak poniżej

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

I modyfikuj także w funkcjonalności 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); 
    }
}

Mam nadzieję, że to może ci pomóc!


To wcale nie jest konieczne.
Xaver Kapeller,

Oryginalna odpowiedź, jeśli tego nie zrobisz, nastąpi wyjątek IndexOutOfBoundsException, więc dlaczego nie jest to konieczne ???? Chcesz dziennik? @XaverKapeller
toidv

Nie będzie wyjątku, jeśli zastosujesz Adapterniewłaściwy sposób. Nie widzę twojego kodu, chyba najbardziej prawdopodobnym problemem jest to, że nie przekazujesz kopii listy ze wszystkimi elementami do Adapter.
Xaver Kapeller,

Dziennik błędów: W / System.err: java.lang.IndexOutOfBoundsException: Niepoprawny indeks 36, rozmiar wynosi 35 W / System.err: at java.util.ArrayList.throwIndexOutOfBoundsException (ArrayList.java:255) W / System.err: at java.util.ArrayList.add (ArrayList.java:147) W / System.err: at com.quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.addItem (MultipleSelectFilterAdapter.java:125) W / System.err: at com .quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.applyAndAnimateAdditions (MultipleSelectFilterAdapter.java:78)
toidv 13.08.16

Pomóż sprawdzić kod źródłowy poniżej @XaverKapeller gist.github.com/toidv/fe71dc45169e4138271b52fdb29420c5
toidv

0

Recyclerview with searchview and clicklistener

Dodaj interfejs do adaptera.

public interface SelectedUser{

    void selectedUser(UserModel userModel);

}

zaimplementuj interfejs w swojej mainactivity i zastąp metodę. @Override public void selectedUser (UserModel userModel) {

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



}

Pełny samouczek i kod źródłowy: Recyclerview with searchview i onclicklistener


-1

Ten sam problem rozwiązałem za pomocą linku z pewnymi modyfikacjami. Filtr wyszukiwania w RecyclerView z kartami. Czy to w ogóle możliwe? (mam nadzieję że to pomoże).

Oto moja klasa adaptera

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

// Klasa filtra

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

}

// Klasa aktywności

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

W metodzie OnQueryTextChangeListener () użyj adaptera. Rzuciłem go na fragmenty, ponieważ mój program jest fragmentowany. Możesz użyć adaptera bezpośrednio, jeśli jest on w twojej klasie aktywności.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.