Implementacja SearchView na pasku akcji

83

Muszę utworzyć SearchViewz mojego arrayList<String>i pokazać sugestie na liście rozwijanej to samo

wprowadź opis obrazu tutaj

Szukam samouczków, które wyjaśniają krok po kroku, jak zbudować SearchViewpasek akcji.

Przeczytałem dokumentację i idąc za przykładem google ale nie było to dla mnie przydatne.

Stworzyłem wyszukiwanie

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_search"
          android:title="Search"
          android:icon="@android:drawable/ic_menu_search"
          android:showAsAction="always"
          android:actionViewClass="android.widget.SearchView" />
</menu>`

Ale nie wiem, jak ustawić parametry tablicy ciągów. Próbowałem pobrać wynik w innym działaniu, ale nie zadziałało.

Matteo
źródło
6
Powinieneś opublikować wszystko, co próbowałeś, tak szczegółowo, jak to tylko możliwe. Konkretne pytania z przykładowym kodem zwykle uzyskują szybsze i bardziej znaczące odpowiedzi.
Collin Flynn,

Odpowiedzi:

128

Znalezienie rozwiązania tego problemu zajęło trochę czasu, ale okazało się, że jest to najłatwiejszy sposób, aby to zadziałało w sposób, który opisałeś. Mogłyby być lepsze sposoby, aby to zrobić, ale ponieważ nie opublikowałeś kodu aktywności, będę musiał improwizować i założyć, że masz taką listę na początku swojej aktywności:

private List<String> items = db.getItems();

ExampleActivity.java

private List<String> items;

private Menu menu;

@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.example, menu);

    this.menu = menu;

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));

        search.setOnQueryTextListener(new OnQueryTextListener() { 

            @Override 
            public boolean onQueryTextChange(String query) {

                loadHistory(query);

                return true; 

            } 

        });

    }

    return true;

}

// History
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void loadHistory(String query) {

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        // Cursor
        String[] columns = new String[] { "_id", "text" };
        Object[] temp = new Object[] { 0, "default" };

        MatrixCursor cursor = new MatrixCursor(columns);

        for(int i = 0; i < items.size(); i++) {

            temp[0] = i;
            temp[1] = items.get(i);

            cursor.addRow(temp);

        }

        // SearchView
        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        final SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSuggestionsAdapter(new ExampleAdapter(this, cursor, items));

    }

}

Teraz musisz utworzyć adapter rozszerzony z CursorAdapter:

ExampleAdapter.java

public class ExampleAdapter extends CursorAdapter {

    private List<String> items;

    private TextView text;

    public ExampleAdapter(Context context, Cursor cursor, List<String> items) {

        super(context, cursor, false);

        this.items = items;

    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        text.setText(items.get(cursor.getPosition()));

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View view = inflater.inflate(R.layout.item, parent, false);

        text = (TextView) view.findViewById(R.id.text);

        return view;

    }

}

Lepszym sposobem na to jest, jeśli dane listy pochodzą z bazy danych, można przekazać Cursorzwracane przez funkcje bazy danych bezpośrednio do ExampleAdapteri użyć odpowiedniego selektora kolumny, aby wyświetlić tekst kolumny w elemencie, do którego TextViewodwołuje się adapter.

Uwaga: podczas importowania CursorAdapternie importuj wersji obsługi systemu Android, android.widget.CursorAdapterzamiast tego zaimportuj standard .

Adapter będzie również wymagał niestandardowego układu:

res / layout / item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <TextView
        android:id="@+id/item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

Teraz można dostosowywać elementy listy, dodając do układu dodatkowe widoki tekstu lub obrazów i wypełniając je danymi w adapterze.

To powinno być wszystko, ale jeśli jeszcze tego nie zrobiłeś, potrzebujesz elementu menu SearchView:

res / menu / example.xml

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

    <item
        android:id="@+id/search"
        android:title="@string/search"
        android:showAsAction="ifRoom"
        android:actionViewClass="android.widget.SearchView" />

</menu>

Następnie utwórz konfigurację z możliwością wyszukiwania:

res / xml / searchable.xml

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search"
    android:hint="@string/search" >
</searchable>

Na koniec dodaj to wewnątrz odpowiedniego tagu aktywności w pliku manifestu:

AndroidManifest.xml

<intent-filter>
    <action android:name="android.intent.action.SEARCH" />
</intent-filter>

<meta-data
    android:name="android.app.default_searchable"
    android:value="com.example.ExampleActivity" />
<meta-data
    android:name="android.app.searchable"
    android:resource="@xml/searchable" />

Uwaga: @string/searchciąg znaków użyty w przykładach powinien być zdefiniowany w values ​​/ strings.xml , nie zapomnij również zaktualizować odniesienia do com.exampleswojego projektu.

tpbapp
źródło
3
Cześć, ale jedna wątpliwość, mam nadzieję, że pomożesz mi w tym. Jeśli wpiszę jedną literę na filtrze, mimo że mam wyniki filtru, nic się nie wyświetli, jeśli jest więcej niż jedna. wtedy tylko się wyświetla. czemu?
Naruto
Cześć, może sprawdź swoje style pod kątem czegoś związanego z EditText lub AutoCompleteTextView, ponieważ możesz ustawić domyślną liczbę znaków, które należy wprowadzić, zanim wyświetli się autouzupełnianie. Właściwie myślę, że wartość domyślna to 2 znaki, więc może być tak, że będziesz musiał dodać to do stylów, aby to zmienić.
tpbapp
Cześć, podczas opracowywania natknąłem się na jeszcze jeden problem widoku wyszukiwania. jeśli wyświetlę więcej pozycji w menu rozwijanym, np .: 3 widoki tekstu, rozmiar pozycji wzrasta, więc elementy wyszukiwania są przesyłane na klawiaturze, jeśli przewijam jak i.stack.imgur.com/d7t3A.png . Dzieje się tak, jeśli zwiększę rozmiar tekstu lub dodam więcej widoków w elemencie. Czy jest to zachowanie domyślne?
Naruto
Cześć ponownie, nie, to nie powinno się dziać. Wygląda na to, że masz już dużo kodu w projekcie, który koliduje z AutoCompleteTextView w SearchView lub być może coś jest nie tak z oprogramowaniem klawiatury i aplikacją lub wtyczką powodującą to zachowanie. Domyślne zachowanie powinno wyglądać następująco: stackoverflow.com/questions/22116237/… z wyjątkiem tego, że ta osoba pyta, jak sprawić, by nie było ono ukryte za klawiaturą.
tpbapp
3
@tpbapp Już to rozgryzłem. Lista sugestii nie pojawia się, ponieważ w kodzie search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));wywołałeś dwukrotnie w metodach onCreateOptionsMenu()i loadHistory(). Dlatego tworzę zmienną globalną SearchView i SearchManager i wywołuję ją tylko getSearchableInfo()raz onCreateOptionsMenu(). Moja lista sugestii działa teraz doskonale. Sugeruję, abyś poprawił swój kod.
Hafizh Herdi
53

Jeśli ktokolwiek inny ma nullptr w zmiennej searchview, dowiedziałem się, że konfiguracja przedmiotu jest nieco inna:

stary:

android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"

Nowy:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView"

pre-android x:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"

Aby uzyskać więcej informacji, zaktualizowana dokumentacja znajduje się tutaj .

Smar
źródło
3
Dziękuję Ci. Właśnie tego mi brakowało. :-)
Morten E. Rasmussen
Dziękuję, że to było proste i pomocne.
Muhammad
1
app:actionViewClass="androidx.appcompat.widget.SearchView"dla androidx
HendraWD
4

SearchDialog czy SearchWidget?

Jeśli chodzi o implementację funkcji wyszukiwania, istnieją dwa sugerowane podejścia w oficjalnej dokumentacji programisty Androida.
Możesz użyć SearchDialog lub SearchWidget .
Mam zamiar wyjaśnić implementację funkcji wyszukiwania za pomocą SearchWidget.

Jak to zrobić z widżetem wyszukiwania?

Wyjaśnię funkcjonalność wyszukiwania w RecyclerView przy użyciu SearchWidget. To całkiem proste.

Po prostu wykonaj 5 prostych kroków

1) Dodaj element searchView w menu

Możesz dodać, SearchViewjak actionVieww menu za pomocą

app: useActionClass = "android.support.v7.widget.SearchView".

<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   tools:context="rohksin.com.searchviewdemo.MainActivity">
   <item
       android:id="@+id/searchBar"
       app:showAsAction="always"
       app:actionViewClass="android.support.v7.widget.SearchView"
   />
</menu>

2) Skonfiguruj tekst podpowiedzi SerchView, odbiornik itp

Powinieneś zainicjować SearchView w onCreateOptionsMenu(Menu menu)metodzie.

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

        MenuItem searchItem = menu.findItem(R.id.searchBar);

        SearchView searchView = (SearchView) searchItem.getActionView();
        searchView.setQueryHint("Search People");
        searchView.setOnQueryTextListener(this);
        searchView.setIconified(false);

        return true;
   }

3) Zaimplementuj SearchView.OnQueryTextListener w swojej aktywności

OnQueryTextListener ma dwie abstrakcyjne metody

  1. onQueryTextSubmit(String query)
  2. onQueryTextChange(String newText

Więc Twój szkielet aktywności wyglądałby tak

YourActivity extends AppCompatActivity implements SearchView.OnQueryTextListener{

     public boolean onQueryTextSubmit(String query)

     public boolean onQueryTextChange(String newText) 

}

4) Zaimplementuj SearchView.OnQueryTextListener

Możesz zapewnić implementację dla metod abstrakcyjnych, takich jak ta

public boolean onQueryTextSubmit(String query) {

    // This method can be used when a query is submitted eg. creating search history using SQLite DB

    Toast.makeText(this, "Query Inserted", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onQueryTextChange(String newText) {

    adapter.filter(newText);
    return true;
}

5) Napisz metodę filtru w adapterze RecyclerView.

Najważniejsza część. Możesz napisać własną logikę wyszukiwania.
Tutaj jest moje. Ten fragment kodu pokazuje listę Name, która zawiera tekst wpisany wSearchView

public void filter(String queryText)
{
    list.clear();

    if(queryText.isEmpty())
    {
       list.addAll(copyList);
    }
    else
    {

       for(String name: copyList)
       {
           if(name.toLowerCase().contains(queryText.toLowerCase()))
           {
              list.add(name);
           }
       }

    }

   notifyDataSetChanged();
}

Odpowiedni link:

Pełny działający kod w SearchView z bazą danych SQLite w tej aplikacji muzycznej

Rohit Singh
źródło
1

Aby Searchviewskorzystać z tego kodu

  1. W przypadku XML

    <android.support.v7.widget.SearchView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/searchView">
    
    </android.support.v7.widget.SearchView>
    

  2. W swoim fragmencie lub aktywności

    package com.example.user.salaryin;
    
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.view.MenuItemCompat;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.SearchView;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Toast;
    import com.example.user.salaryin.Adapter.BusinessModuleAdapter;
    import com.example.user.salaryin.Network.ApiClient;
    import com.example.user.salaryin.POJO.ProductDetailPojo;
    import com.example.user.salaryin.Service.ServiceAPI;
    import java.util.ArrayList;
    import java.util.List;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    
    
    public class OneFragment extends Fragment implements SearchView.OnQueryTextListener {
    
    RecyclerView recyclerView;
    RecyclerView.LayoutManager layoutManager;
    ArrayList<ProductDetailPojo> arrayList;
    BusinessModuleAdapter adapter;
    private ProgressDialog pDialog;
    GridLayoutManager gridLayoutManager;
    SearchView searchView;
    
    public OneFragment() {
        // Required empty public constructor
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    
        View rootView = inflater.inflate(R.layout.one_fragment,container,false);
    
        pDialog = new ProgressDialog(getActivity());
        pDialog.setMessage("Please wait...");
    
    
        searchView=(SearchView)rootView.findViewById(R.id.searchView);
        searchView.setQueryHint("Search BY Brand");
        searchView.setOnQueryTextListener(this);
    
        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
        layoutManager = new LinearLayoutManager(this.getActivity());
        recyclerView.setLayoutManager(layoutManager);
        gridLayoutManager = new GridLayoutManager(this.getActivity().getApplicationContext(), 2);
        recyclerView.setLayoutManager(gridLayoutManager);
        recyclerView.setHasFixedSize(true);
        getImageData();
    
    
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.one_fragment, container, false);
        return rootView;
    }
    
    
    private void getImageData() {
        pDialog.show();
        ServiceAPI service = ApiClient.getRetrofit().create(ServiceAPI.class);
        Call<List<ProductDetailPojo>> call = service.getBusinessImage();
    
        call.enqueue(new Callback<List<ProductDetailPojo>>() {
            @Override
            public void onResponse(Call<List<ProductDetailPojo>> call, Response<List<ProductDetailPojo>> response) {
                if (response.isSuccessful()) {
                    arrayList = (ArrayList<ProductDetailPojo>) response.body();
                    adapter = new BusinessModuleAdapter(arrayList, getActivity());
                    recyclerView.setAdapter(adapter);
                    pDialog.dismiss();
                } else if (response.code() == 401) {
                    pDialog.dismiss();
                    Toast.makeText(getActivity(), "Data is not found", Toast.LENGTH_SHORT).show();
                }
    
            }
    
            @Override
            public void onFailure(Call<List<ProductDetailPojo>> call, Throwable t) {
                Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
                pDialog.dismiss();
    
            }
        });
    }
    
       /* @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        getActivity().getMenuInflater().inflate(R.menu.menu_search, menu);
        MenuItem menuItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(menuItem);
        searchView.setQueryHint("Search Product");
        searchView.setOnQueryTextListener(this);
    }*/
    
    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }
    
    @Override
    public boolean onQueryTextChange(String newText) {
        newText = newText.toLowerCase();
        ArrayList<ProductDetailPojo> newList = new ArrayList<>();
        for (ProductDetailPojo productDetailPojo : arrayList) {
            String name = productDetailPojo.getDetails().toLowerCase();
    
            if (name.contains(newText) )
                newList.add(productDetailPojo);
            }
        adapter.setFilter(newList);
        return true;
       }
    }
    
  3. W klasie adapterów

     public void setFilter(List<ProductDetailPojo> newList){
        arrayList=new ArrayList<>();
        arrayList.addAll(newList);
        notifyDataSetChanged();
    }
    
Neeraj Gupta
źródło