por - metodo buscar android studio
¿Cómo construir Gmail como cuadro de búsqueda en la barra de acción? (7)
Actualmente estoy usando el widget ActionBarcompat
dentro de ActionBarcompat
para filtrar una lista durante la búsqueda.
Cuando el usuario comienza a ingresar texto, el ListView en el diseño principal se actualiza con un adaptador para filtrar los resultados. Hago esto al implementar un OnQueryTextListener
y filtrar los resultados en cada golpe de tecla.
En su lugar, quiero crear un cuadro de búsqueda similar a Gmail con una lista de sugerencias automática generada y sin cambios en la vista subyacente
He seguido este tutorial que utiliza el componente SearchView
pero requiere una actividad de búsqueda . Quiero que la lista desplegable esté sobre MainActivity donde tengo el ListView (como en la aplicación de Gmail) y no una actividad dedicada.
Además, implementarlo de la misma manera que en el tutorial parece una exageración para lo que quiero (solo un menú desplegable)
Consulte este ejemplo que implementa exactamente lo que solicitó: http://wptrafficanalyzer.in/blog/android-searchview-widget-with-actionbarcompat-library/
Debe usar ListPopupWindow y anclarlo al widget de vista de búsqueda.
He utilizado con éxito la respuesta de Michaels ( https://.com/a/18894726/2408033 ) pero no me gustó el manual, inflando las vistas y agregándolas a la barra de acción, alternando su estado, etc.
Lo modifiqué para usar un ActionBar ActionView en lugar de agregar la vista manualmente a la barra de acción / barra de herramientas.
Me parece que esto funciona mucho mejor, ya que no necesito administrar el estado de apertura / cierre y la ocultación de las vistas, como lo hizo en su ejemplo en el método toggleSearch en el enlace agregado. También funciona perfectamente con el botón Atrás.
En mi menu.xml
<item
android:id="@+id/global_search"
android:icon="@android:drawable/ic_menu_search"
android:title="Search"
app:actionLayout="@layout/actionbar_search"
app:showAsAction="ifRoom|collapseActionView" />
En mi onCreateOptionsMenu
View actionView = menu.findItem(R.id.global_search).getActionView();
searchTextView = (ClearableAutoCompleteTextView) actionView.findViewById(R.id.search_box);
searchTextView.setAdapter(searchAdapter);
Puede encontrar una versión completamente funcional de la implementación en mi proyecto. Tenga en cuenta que hay dos vistas de búsqueda, ya que estaba usando el SearchView real para filtrar un listView.
Necesitas usar el atributo collapseActionView
.
El atributo collapseActionView permite que SearchView se expanda para ocupar toda la barra de acción y se contraiga en un elemento de la barra de acción normal cuando no está en uso.
Por ejemplo:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/search"
android:title="@string/search_title"
android:icon="@drawable/ic_search"
android:showAsAction="collapseActionView|ifRoom"
android:actionViewClass="android.widget.SearchView" />
</menu>
Si desea implementar solo el efecto desplegable, vaya a AutoCompleteTextView
Y puedes encontrar un buen tutorial here
Luego, si desea implementar el diseño exacto, debe implementar ActionBar y si desea implementar una versión más baja, debe implementar ActionBarCombat lugar de ActionBar
Si solo desea un componente que haga lo que se describe en la pregunta, sugiero esta library . También puede implementar la interfaz de búsqueda fuera de bx, sin embargo, tenga en cuenta que tiene limitaciones de IU:
Para implementar una interfaz similar a la aplicación de Gmail, deberá comprender los conceptos de:
- Proveedores de contenido;
- Datos persistentes en SQLite
- Listview o RecyclerView y sus adaptadores;
- Pasar datos entre actividades;
El resultado final debe verse algo como:
Hay muchas (muchas) formas de obtener el mismo resultado (o mejor), voy a describir una posible forma.
Parte 01: Diseño
Decidí administrar toda la interfaz en una nueva actividad, para eso he creado tres diseños XML:
custom_searchable.xml : custom_searchable.xml todos los elementos de la IU en un RelativeLayout que servirá como contenido para SearchActivity;
<include android:id="@+id/cs_header" layout="@layout/custom_searchable_header_layout" /> <android.support.v7.widget.RecyclerView android:id="@+id/cs_result_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:stackFromBottom="true" android:transcriptMode="normal"/>
custom_searchable_header_layout.xml : contiene la barra de búsqueda donde el usuario escribirá su consulta. También contendrá el micrófono, borrar y devolver btn;
<RelativeLayout android:id="@+id/custombar_return_wrapper" android:layout_width="55dp" android:layout_height="fill_parent" android:gravity="center_vertical" android:background="@drawable/right_oval_ripple" android:focusable="true" android:clickable="true" > <ImageView android:id="@+id/custombar_return" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:background="#00000000" android:src="@drawable/arrow_left_icon"/> </RelativeLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@+id/custombar_return_wrapper" android:layout_marginRight="60dp" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp"> <EditText android:id="@+id/custombar_text" android:layout_width="match_parent" android:layout_height="match_parent" android:hint="Search..." android:textColor="@color/textPrimaryColor" android:singleLine="true" android:imeOptions="actionSearch" android:background="#00000000"> <requestFocus/> </EditText> </android.support.design.widget.TextInputLayout> <RelativeLayout android:id="@+id/custombar_mic_wrapper" android:layout_width="55dp" android:layout_height="fill_parent" android:layout_alignParentRight="true" android:gravity="center_vertical" android:background="@drawable/left_oval_ripple" android:focusable="true" android:clickable="true" > <ImageView android:id="@+id/custombar_mic" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:background="#00000000" android:src="@drawable/mic_icon"/> </RelativeLayout>
custom_searchable_row_details.xml : contiene los elementos de la IU que se mostrarán en la lista de resultados que se mostrarán en respuesta a la consulta del usuario;
<ImageView android:id="@+id/rd_left_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dp" android:layout_centerVertical="true" android:layout_marginLeft="5dp" android:src="@drawable/clock_icon" /> <LinearLayout android:id="@+id/rd_wrapper" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:layout_toRightOf="@+id/rd_left_icon" android:layout_marginLeft="20dp" android:layout_marginRight="50dp"> <TextView android:id="@+id/rd_header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/textPrimaryColor" android:text="Header" android:textSize="16dp" android:textStyle="bold" android:maxLines="1"/> <TextView android:id="@+id/rd_sub_header_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/textPrimaryColor" android:text="Sub Header" android:textSize="14dp" android:maxLines="1" /> </LinearLayout> <ImageView android:id="@+id/rd_right_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@drawable/arrow_left_up_icon"/>
Parte 02: Implementando la actividad de búsqueda
La idea es que, cuando el usuario escribe el botón de búsqueda (que puede colocar donde quiera), se SearchActivity esta actividad de SearchActivity . Tiene algunas responsabilidades principales:
Enlazar a los elementos de la interfaz de usuario en el custom_searchable_header_layout.xml : al hacer eso, es posible:
para proporcionar escuchas para el EditText (donde el usuario escribirá su consulta):
TextView.OnEditorActionListener searchListener = new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) { // do processing } } searchInput.setOnEditorActionListener(searchListener); searchInput.addTextChangedListener(new TextWatcher() { public void onTextChanged(final CharSequence s, int start, int before, int count) { // Do processing } }
agregar un oyente para el botón de retorno (que por su turno solo llamará a finish () y volverá a la actividad de la persona que llama):
this.dismissDialog.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { finish(); }
llama a la intención de la API de voz a texto de Google:
private void implementVoiceInputListener () { this.voiceInput.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (micIcon.isSelected()) { searchInput.setText(""); query = ""; micIcon.setSelected(Boolean.FALSE); micIcon.setImageResource(R.drawable.mic_icon); } else { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak now"); SearchActivity.this.startActivityForResult(intent, VOICE_RECOGNITION_CODE); } } }); }
Proveedor de contenido
Al crear una interfaz de búsqueda, el desarrollador tiene típicamente dos opciones:
- Sugiera consultas recientes al usuario: esto implica que cada vez que el usuario realiza una búsqueda, la consulta escrita se conservará en una base de datos que se recuperará más adelante como una sugerencia para futuras búsquedas;
- Sugiera opciones personalizadas al usuario: el desarrollador intentará predecir lo que quiere el usuario procesando las letras ya escritas;
En ambos casos, las respuestas se devolverán como un objeto Cursor que mostrará su contenido como ítems en la lista de resultados. Todo este proceso se puede implementar utilizando la API del proveedor de contenido. Los detalles sobre cómo usar los Proveedores de contenido se pueden encontrar en este link .
En el caso de que el desarrollador desee implementar el comportamiento descrito en 1., puede ser útil usar la estrategia de extinging la clase SearchRecentSuggestionsProvider. Los detalles sobre cómo hacerlo se pueden encontrar en este link .
Implementando la interfaz de búsqueda
Esta interfaz debe proporcionar el siguiente comportamiento:
Cuando el usuario escribe una letra, una llamada al método de consulta de la clase del proveedor de contenido recuperado debe devolver un cursor lleno con la sugerencia que se muestra en la lista. Debe tomar para no congelar el hilo de la IU, por lo que le recomiendo que lo haga. buscar en una AsyncTask:
public void onTextChanged(final CharSequence s, int start, int before, int count) { if (!"".equals(searchInput.getText().toString())) { query = searchInput.getText().toString(); setClearTextIcon(); if (isRecentSuggestionsProvider) { // Provider is descendant of SearchRecentSuggestionsProvider mapResultsFromRecentProviderToList(); // query is performed in this method } else { // Provider is custom and shall follow the contract mapResultsFromCustomProviderToList(); // query is performed in this method } } else { setMicIcon(); } }
Dentro del método onPostExecute () de su AsyncTask, debe recuperar una lista (que debe provenir del método doInBackground ()) que contiene los resultados que se mostrarán en la Lista de resultados (puede asignarlo en una clase POJO y pasarlo a su medida) adaptador o puede usar un CursorAdapter que sería la mejor práctica para esta tarea):
protected void onPostExecute(List resultList) { SearchAdapter adapter = new SearchAdapter(resultList); searchResultList.setAdapter(adapter); } protected List doInBackground(Void[] params) { Cursor results = results = queryCustomSuggestionProvider(); List<ResultItem> resultList = new ArrayList<>(); Integer headerIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); Integer subHeaderIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); Integer leftIconIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); Integer rightIconIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); while (results.moveToNext()) { String header = results.getString(headerIdx); String subHeader = (subHeaderIdx == -1) ? null : results.getString(subHeaderIdx); Integer leftIcon = (leftIconIdx == -1) ? 0 : results.getInt(leftIconIdx); Integer rightIcon = (rightIconIdx == -1) ? 0 : results.getInt(rightIconIdx); ResultItem aux = new ResultItem(header, subHeader, leftIcon, rightIcon); resultList.add(aux); } results.close(); return resultList;
Identifique cuándo el usuario toca el botón de búsqueda desde el teclado virtual. Cuando lo haga, envíe un intento a la actividad de búsqueda (la responsable de manejar el resultado de búsqueda) y agregue la consulta como información adicional en el intento.
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case VOICE_RECOGNITION_CODE: { if (resultCode == RESULT_OK && null != data) { ArrayList<String> text = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); searchInput.setText(text.get(0)); } break; } } }
Identifique cuándo el usuario hace clic en una de las sugerencias mostradas y envíe e intente que contenga la información del elemento (esta intención debe ser diferente de la del paso anterior)
private void sendSuggestionIntent(ResultItem item) { try { Intent sendIntent = new Intent(this, Class.forName(searchableActivity)); sendIntent.setAction(Intent.ACTION_VIEW); sendIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); Bundle b = new Bundle(); b.putParcelable(CustomSearchableConstants.CLICKED_RESULT_ITEM, item); sendIntent.putExtras(b); startActivity(sendIntent); finish(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
Los pasos descritos deben ser suficientes para implementar una interfaz yourseld. Todos los ejemplos de código fueron tomados de library . He hecho esta biblioteca que hace bastante de lo que se describe anteriormente. Aún no está bien probado y es posible que parte de la configuración de la interfaz de usuario aún no esté disponible.
Espero que esta respuesta pueda ayudar a alguien en necesidad.
He creado un pequeño tutorial para hacer eso
http://drzon.net/how-to-create-a-clearable-autocomplete-dropdown-with-autocompletetextview/
Visión general
Tuve que reemplazar el SearchView
con AutoCompleteTextView
como se sugiere.
Primero, crea un adaptador. En mi caso fue un JSONObject
ArrayAdapter. Los datos que quería mostrar en el menú desplegable eran el nombre del lugar y la dirección del lugar. Observe que el adaptador debe ser Filtarable
y anular getFilter()
// adapter for the search dropdown auto suggest
ArrayAdapter<JSONObject> searchAdapter = new ArrayAdapter<JSONObject>(this, android.R.id.text1) {
private Filter filter;
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = this.getLayoutInflater().inflate(R.layout.search_item, parent, false);
}
TextView venueName = (TextView) convertView.findViewById(R.id.search_item_venue_name);
TextView venueAddress = (TextView) convertView.findViewById(R.id.search_item_venue_address);
final JSONObject venue = this.getItem(position);
convertView.setTag(venue);
try {
CharSequence name = highlightText(venue.getString("name"));
CharSequence address = highlightText(venue.getString("address"));
venueName.setText(name);
venueAddress.setText(address);
}
catch (JSONException e) {
Log.i(Consts.TAG, e.getMessage());
}
return convertView;
}
@Override
public Filter getFilter() {
if (filter == null) {
filter = new VenueFilter();
}
return filter;
}
};
Aquí está el VenueFilter
personalizado:
private class VenueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<JSONObject> list = new ArrayList<JSONObject>(venues);
FilterResults result = new FilterResults();
String substr = constraint.toString().toLowerCase();
if (substr == null || substr.length() == 0) {
result.values = list;
result.count = list.size();
} else {
final ArrayList<JSONObject> retList = new ArrayList<JSONObject>();
for (JSONObject venue : list) {
try {
if (venue.getString("name").toLowerCase().contains(constraint) || venue.getString("address").toLowerCase().contains(constraint) ||
{
retList.add(venue);
}
} catch (JSONException e) {
Log.i(Consts.TAG, e.getMessage());
}
}
result.values = retList;
result.count = retList.size();
}
return result;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
searchAdapter.clear();
if (results.count > 0) {
for (JSONObject o : (ArrayList<JSONObject>) results.values) {
searchAdapter.add(o);
}
}
}
}
Ahora configure el diseño para el cuadro de búsqueda ( actionbar_search.xml
):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_horizontal"
android:focusable="true" >
<AutoCompleteTextView
android:id="@+id/search_box"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:dropDownVerticalOffset="5dp"
android:dropDownWidth="wrap_content"
android:inputType="textAutoComplete|textAutoCorrect"
android:popupBackground="@color/white"
android:textColor="#FFFFFF" >
</AutoCompleteTextView>
</RelativeLayout>
Y el diseño del elemento desplegable individual (nombre del lugar y dirección del lugar). Este se ve mal, tendrás que personalizarlo:
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAlignment="gravity" >
<TextView
android:id="@+id/search_item_venue_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/cyan"
android:layout_gravity="right" />
<TextView
android:id="@+id/search_item_venue_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/search_item_venue_name"
android:gravity="right"
android:textColor="@color/white" />
</RelativeLayout>
A continuación queremos ponerlo en la barra de acción.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO | ActionBar.DISPLAY_SHOW_HOME
| ActionBar.DISPLAY_HOME_AS_UP);
LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.actionbar_search, null);
AutoCompleteTextView textView = (AutoCompleteTextView) v.findViewById(R.id.search_box);
textView.setAdapter(searchAdapter);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something when the user clicks
}
});
actionBar.setCustomView(v);
}
De eso se trata, todavía tengo algunas cosas que resolver:
- Esto pone una búsqueda "siempre allí" en la barra de acción, quiero que sea como el widget
SearchView
: un lente de aumento que se abre en un cuadro de búsqueda al hacer clic en él (y tiene un pequeño botónX
para descartarlo y regresar) a la normalidad - Aún no he descubierto cómo personalizar el cuadro desplegable, por ejemplo, el de Gmail parece tener sombra, el mío es simplemente plano, cambia el color de los delimitadores de línea, etc.
En general, esto ahorra toda la sobrecarga de crear una actividad de búsqueda . Por favor agregue si sabe cómo personalizarlo, etc.