Implementar SearchView en la barra de acciones

82

Necesito crear SearchViewdesde mi arrayList<String>y mostrar las sugerencias en la lista desplegable igual que esto

ingrese la descripción de la imagen aquí

Busco tutoriales que expliquen paso a paso cómo construir una SearchViewbarra de acciones.

He leído la documentación y siguiendo el ejemplo de google pero no me ha resultado útil.

He creado la búsqueda

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

Pero no sé cómo configurar los parámetros de la matriz de cadenas. Intenté recuperar el resultado en una actividad diferente pero no funcionó.

Matteo
fuente
6
Debes publicar todo lo que has probado, con el mayor detalle posible. Las preguntas específicas con código de muestra tienden a obtener respuestas más rápidas y significativas.
Collin Flynn

Respuestas:

127

Me tomó un tiempo armar una solución para esto, pero he descubierto que esta es la forma más fácil de hacer que funcione de la manera que usted describe. Podría haber mejores formas de hacer esto, pero como no ha publicado su código de actividad, tendré que improvisar y asumir que tiene una lista como esta al comienzo de su actividad:

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

    }

}

Ahora necesita crear un adaptador extendido desde 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;

    }

}

Una mejor manera de hacerlo es si los datos de su lista provienen de una base de datos, puede pasar las Cursorfunciones devueltas por la base de datos directamente ExampleAdaptery usar el selector de columna correspondiente para mostrar el texto de la columna en el que se hace TextViewreferencia en el adaptador.

Tenga en cuenta: cuando importe CursorAdapter, no importe la versión de soporte de Android, importe el estándar en su android.widget.CursorAdapterlugar.

El adaptador también requerirá un diseño personalizado:

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>

Ahora puede personalizar los elementos de la lista agregando texto o vistas de imágenes adicionales al diseño y llenándolos con datos en el adaptador.

Esto debería ser todo, pero si aún no lo ha hecho, necesita un elemento de menú 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>

Luego, cree una configuración de búsqueda:

res / xml / seek.xml

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

Finalmente, agregue esto dentro de la etiqueta de actividad relevante en el archivo de manifiesto:

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

Tenga en cuenta: La @string/searchcadena utilizada en los ejemplos debe definirse en values ​​/ strings.xml , también no olvide actualizar la referencia a com.examplepara su proyecto.

tpbapp
fuente
3
Hola, pero una duda, espero que me ayudes con esto. Si escribo una letra en el filtro, aunque tengo resultados de filtro, no se mostrará nada, si es más de una. entonces solo se muestra. ¿por qué?
Naruto
Hola, tal vez revise sus estilos para cualquier cosa relacionada con EditText o AutoCompleteTextView, ya que puede establecer el número predeterminado de caracteres que deben ingresarse antes de que se muestre el autocompletado. En realidad, creo que el valor predeterminado es de 2 caracteres, por lo que podría ser el caso de que debas agregar esto a los estilos para cambiarlo.
tpbapp
Hola, mientras desarrollaba me encontré con un problema más de searchview. si muestro más elementos en el menú desplegable, por ejemplo: 3 vistas de texto, el tamaño del elemento aumenta, por lo que los elementos de búsqueda fluyen en el teclado si me desplazo como i.stack.imgur.com/d7t3A.png . Esto sucede si aumento el tamaño del texto o agrego más vistas en el elemento. ¿Es un comportamiento predeterminado?
Naruto
Hola de nuevo, no, eso no debería estar pasando. Parece que ya tiene mucho código en el proyecto que está interfiriendo con AutoCompleteTextView en SearchView o tal vez hay algo mal con el software del teclado y la aplicación o un complemento que causa este comportamiento. El comportamiento predeterminado debería ser algo así como stackoverflow.com/questions/22116237/… excepto que esta persona está preguntando cómo hacer que no se oculte detrás del teclado.
tpbapp
3
@tpbapp Ya lo he descubierto. La razón por la que la lista de sugerencias no aparece es porque en su código había search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));llamado dos veces en los métodos onCreateOptionsMenu()y loadHistory(). Así que hago la variable global SearchView y SearchManager, y solo llamo getSearchableInfo()una vez en onCreateOptionsMenu(). Y mi lista de sugerencias ahora funciona perfectamente. Le sugiero que revise su código.
Hafizh Herdi
52

Si alguien más tiene un nullptr en la variable de vista de búsqueda, descubrí que la configuración del elemento es un poco diferente:

antiguo:

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

nuevo:

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

pre-android x:

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

Para obtener más información, su documentación actualizada se encuentra aquí .

Grasa
fuente
3
Gracias. Justo lo que me estaba perdiendo. :-)
Morten E. Rasmussen
Gracias, eso fue sencillo y útil.
Muhammad
1
app:actionViewClass="androidx.appcompat.widget.SearchView"para androidx
HendraWD
3

SearchDialog o SearchWidget?

Cuando se trata de implementar una función de búsqueda, hay dos enfoques sugeridos por la documentación oficial para desarrolladores de Android.
Puede utilizar un SearchDialog o un SearchWidget .
Voy a explicar la implementación de la funcionalidad de búsqueda usando SearchWidget.

¿Cómo hacerlo con el widget de búsqueda?

Explicaré la funcionalidad de búsqueda en un RecyclerView usando SearchWidget. Es bastante sencillo.

Solo sigue estos 5 sencillos pasos

1) Agregar elemento searchView en el menú

Puede agregar SearchViewse puede agregar como actionViewen el menú usando

aplicación: 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) Configure el texto de la sugerencia de SerchView, el oyente, etc.

Debe inicializar SearchView en el onCreateOptionsMenu(Menu menu)método.

  @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) Implemente SearchView.OnQueryTextListener en su actividad

OnQueryTextListener tiene dos métodos abstractos

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

Entonces tu esqueleto de actividad se vería así

YourActivity extends AppCompatActivity implements SearchView.OnQueryTextListener{

     public boolean onQueryTextSubmit(String query)

     public boolean onQueryTextChange(String newText) 

}

4) Implementar SearchView.OnQueryTextListener

Puede proporcionar la implementación para los métodos abstractos como este

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) Escriba un método de filtro en su adaptador RecyclerView.

Parte más importante. Puede escribir su propia lógica para realizar la búsqueda.
Aquí esta el mio. Este fragmento muestra la lista de Nombre que contiene el texto escrito en elSearchView

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

Enlace relevante:

Código de trabajo completo en SearchView con una base de datos SQLite en esta aplicación de música

Rohit Singh
fuente
1

Para Searchviewusar este código

  1. Para 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. En tu Fragmento o Actividad

    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. En clase de adaptador

     public void setFilter(List<ProductDetailPojo> newList){
        arrayList=new ArrayList<>();
        arrayList.addAll(newList);
        notifyDataSetChanged();
    }
    
Neeraj Gupta
fuente