SearchView en ActionBar – problemas con el botón * Up *

Estoy utilizando el SearchView en el ActionBar del ListView . La lupa se puede tocar, el SearchView muestra su cuadro de edición, y el usuario puede introducir el texto para filtrar el contenido de la lista. Casi funciona. Sin embargo, cuando el usuario presiona el botón Arriba , el SearchView contrae de nuevo al icono, el texto dentro del widget se borra y el filtrado se restablece. El efecto (en mi caso) es que la lista puede ser filtrada sólo cuando el SearchView no está iconificado. El comportamiento deseado es mantener el texto del filtro también después de que se derrumbó el SearchView .

Atención: El comportamiento probablemente cambió en Android 4.3. Con 4.2.2 funcionó como quería. Vea las observaciones a continuación.

Detalles: Para ser más específico, el menú contiene el siguiente elemento:

 <item android:id="@+id/menu_search_customers" android:title="@string/menu_search_text" android:icon="@android:drawable/ic_menu_search" android:showAsAction="ifRoom|collapseActionView" android:actionViewClass="android.widget.SearchView" /> 

Observe el icono y el android:showAsAction . Creo que el botón Arriba aparece de forma predeterminada cuando se amplía el SearchView (por arriba quiero decir el < más el icono – vea la imagen derecha con el libro azul de la navegación oficial con Back and Up ). Parece que la implementación del manejador por defecto colapsa el SearchView ampliado (vuelve al estado del icono).

El ejemplo de botón * Up * en la imagen derecha

Al depurar, he encontrado que el onQueryTextChange() es encendido con el texto vacío cuando se utiliza el Up . (Creo que este no fue el caso con Android 4.2.2, ya que funcionó como quería antes de la actualización del SO). Esta es la razón por la que el filtrado de los elementos de la lista también se restablece – vea mi onQueryTextChange() continuación. Quiero que el SearchView contraiga y el texto del filtro se muestre como subtítulo en la barra de acción.

Hasta ahora, mi código relacionado con el SearchView parece a esto:

 @Override public boolean onCreateOptionsMenu(Menu menu) { // MenuInflater adds the magnifying glass icon for the SearchView // to the ActionBar as the always visible menu item. MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.customers_menu, menu); // Get the related SearchView widget. SearchView sv = (SearchView) menu.findItem(R.id.menu_search_customers) .getActionView(); // Get the changes immediately. sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { // I am not sure whether the onQueryTextSubmit() is important // for the purpose. @Override public boolean onQueryTextSubmit(String query) { getActionBar().setSubtitle(mCurFilter); return true; } @Override public boolean onQueryTextChange(String newText) { // The newText is stored into a member variable that // is used when the new CursorLoader is created. mCurFilter = newText; getActionBar().setSubtitle(mCurFilter); getLoaderManager().restartLoader(0, null, CustomersOverviewActivity.this); return true; } }); return true; } 

El cargador reiniciado llama al onCreateLoader . Observe que el mCurFilter se utiliza para crear la consulta SQL:

 @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String[] projection = { CustomerTable._ID, CustomerTable.CODE, CustomerTable.NAME, CustomerTable.STREET, CustomerTable.TOWN }; String selection = null; // init String[] selectionArgs = null; // init if ( ! mCurFilter.isEmpty()) { selection = CustomerTable.NAME + " like ?"; selectionArgs = new String[]{ "%" + mCurFilter +"%" }; } CursorLoader cursorLoader = new CursorLoader(this, DemoContentProvider.CUSTOMERS_CONTENT_URI, projection, selection, selectionArgs, orderInfo); return cursorLoader; } 

Me gustaría detectar la situación cuando se pulsa el botón Arriba antes de onQueryTextChange() . De esta manera (por ejemplo) podría establecer una bandera y bloquear la asignación mCurFilter por el contenido mCurFilter vacío. Además, cuando se vuelve a expandir el icono de búsqueda, me gustaría inicializar el texto en el mCurFilter ampliado del mCurFilter antes de que se muestre (es decir, la vista expandida está preestablecida con el texto del filtro). ¿Cómo se puede hacer eso?

Actualización: La implementación anterior del SearchView tenía …

 @Override public void onActionViewCollapsed() { clearFocus(); updateViewsVisibility(true); mQueryTextView.setImeOptions(mCollapsedImeOptions); mExpandedInActionView = false; } 

Ahora, contiene …

 @Override public void onActionViewCollapsed() { setQuery("", false); clearFocus(); updateViewsVisibility(true); mQueryTextView.setImeOptions(mCollapsedImeOptions); mExpandedInActionView = false; } 

¿Sabe cuál podría ser la razón para establecer la consulta en la cadena vacía? ¿Debo anular la nueva implementación por el código antiguo? ¿O hay un mejor camino?

He escrito un StatefulSearchView que conserva el texto:

 import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.SearchView; import android.widget.SearchView.OnQueryTextListener; import android.widget.TextView; public class StatefulSearchView extends SearchView implements android.view.View.OnLayoutChangeListener, OnQueryTextListener,android.widget.SearchView.OnCloseListener{ private boolean mSaveText=true; private OnQueryTextListener mQueryListener; private String mQuery; private OnCloseListener mCloseListener; private boolean fromIconify = true; public StatefulSearchView(Context context, AttributeSet attrs) { super(context, attrs); addOnLayoutChangeListener(this); super.setOnCloseListener(this); } public StatefulSearchView(Context context) { super(context); // TODO Auto-generated constructor stub addOnLayoutChangeListener(this); super.setOnCloseListener(this); } public void setSaveSearchTextState(boolean save){ this.mSaveText = save; this.setSaveEnabled(mSaveText); } public void setOnStatefulQueryTextListener(OnQueryTextListener listener) { mQueryListener = listener; super.setOnQueryTextListener(this); } @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if(super.isIconfiedByDefault() || !super.isIconified() && !TextUtils.isEmpty(mQuery) && mSaveText){ setSavedText(mQuery); } Log.i("onLayoutChanged()",""+mQuery); } @Override public void setIconified(boolean iconify) { mQuery = getQuery().toString(); Log.i("setIconified()",""+mQuery); super.setOnCloseListener(null); super.setIconified(iconify); super.setIconified(iconify); super.setOnCloseListener(this); fromIconify = true; } @Override public void setOnCloseListener(OnCloseListener listener) { mCloseListener = listener; super.setOnCloseListener(this); } @Override protected Parcelable onSaveInstanceState() { Parcelable state = super.onSaveInstanceState(); return new SearchQueryState(state, mQuery, mSaveText); } @Override protected void onRestoreInstanceState(Parcelable state) { SearchQueryState sqs = (SearchQueryState)state; super.onRestoreInstanceState(sqs.getSuperState()); mQuery = sqs.getSavedQuery(); mSaveText = sqs.getSaveText(); } @Override public boolean onQueryTextChange(String arg0) { mQuery = arg0; return mQueryListener.onQueryTextChange(mQuery); } @Override public boolean onQueryTextSubmit(String arg0) { // TODO Auto-generated method stub return mQueryListener.onQueryTextSubmit(arg0); } private TextView getTextView(){ int searchTextViewId = getContext().getResources().getIdentifier("android:id/search_src_text", null, null); return (TextView) this.findViewById(searchTextViewId); } private void setSavedText(String s){ super.setOnQueryTextListener(null); Log.i("setSavedText()",""+s); TextView t = getTextView(); t.setText(s); if(!TextUtils.isEmpty(s)) ((EditText)t).setSelection(s.length()); super.setOnQueryTextListener(mQueryListener); } private class SearchQueryState extends BaseSavedState{ private boolean mSaveText; private String mQueryText; public SearchQueryState(Parcel arg0) { super(arg0); this.mQueryText = arg0.readString(); this.mSaveText = arg0.readInt() == 1; } public SearchQueryState(Parcelable superState, String queryText, boolean saveText) { super(superState); this.mQueryText = queryText; this.mSaveText = saveText; } public boolean getSaveText(){ return this.mSaveText; } public String getSavedQuery(){ return mQueryText; } @Override public void writeToParcel(Parcel dest, int flags) { // TODO Auto-generated method stub super.writeToParcel(dest, flags); dest.writeString(mQueryText); dest.writeInt(mSaveText? 1: 0); } } @Override public boolean onClose() { Log.i("onClose()", "Is from setIconified(): "+fromIconify); if(!fromIconify){ mQuery = null; fromIconify = false; } return mCloseListener == null ? false : mCloseListener.onClose(); } } 

En actividad de demostración:

 public class MainActivity extends Activity{ private StatefulSearchView mSearchView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getActionBar().setHomeButtonEnabled(true); } @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if(item.getItemId()==android.R.id.home) { mSearchView.setIconified(true); return true; } return super.onMenuItemSelected(featureId, item); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); MenuItem item = menu.findItem(R.id.action_search); mSearchView =(StatefulSearchView)item.getActionView(); mSearchView.setSaveSearchTextState(true); mSearchView.setOnStatefulQueryTextListener(new OnQueryTextListener(){ @Override public boolean onQueryTextChange(String newText) { // TODO Auto-generated method stub return false; } @Override public boolean onQueryTextSubmit(String query) { // TODO Auto-generated method stub return false; }}); return true; } 

En el menú xml:

 <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_search" android:orderInCategory="100" android:showAsAction="always" android:actionViewClass="com.nikola.despotoski.saveablesearchview.StatefulSearchView" android:title="@string/action_settings"/> </menu> 

En la fuente del SearchView , se dice claramente que cambian el texto a "" :

 @Override public void onActionViewCollapsed() { setQuery("", false); clearFocus(); updateViewsVisibility(true); mQueryTextView.setImeOptions(mCollapsedImeOptions); mExpandedInActionView = false; } /** * {@inheritDoc} */ @Override public void onActionViewExpanded() { if (mExpandedInActionView) return; mExpandedInActionView = true; mCollapsedImeOptions = mQueryTextView.getImeOptions(); mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN); mQueryTextView.setText(""); setIconified(false); } 

Déjame saber si tienes problemas.

No estoy seguro de entender su problema, pero sólo puede detectar cuando se hace clic en esto:

 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: doSOmething(); return true; } return super.onOptionsItemSelected(item); } 

Si interceptas el clic, puedes presumiblemente hacer lo que quieras aquí. Devolver verdadero consumirá el evento y que debe evitar cualquier acción por defecto de tener lugar. De esta manera usted puede hacer lo que quiera que el botón de arriba para hacer mientras al mismo tiempo consumir el evento para evitar el borrado de sus filtros.

Lucho con esto un poco hasta que encontré la solución.

Declare su menuItem como este, compruebe el atributo showAsAction, escriba ifRoom solo, si establece collapseActionView el widget se contraerá y mostrará el botón Atrás en la barra de acción

 <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/search" android:actionViewClass="android.widget.SearchView" android:icon="@drawable/ic_2_action_search" android:showAsAction="ifRoom" android:title="@null"/> </menu> 

Configuración de su SearchView como de costumbre, recuerde que para agregar setIconifiedByDefault esto hará que el icono para iniciar como un icono

 SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE); searchView = (SearchView) menu.findItem(R.id.search).getActionView(); searchView.setIconifiedByDefault(true); searchView.setOnQueryTextListener(new SearchViewOnQueryListener()); searchView.setOnCloseListener(new SearchViewOnCloseListener()); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); 

En su QueryListener es donde se maneja el final de su búsqueda como así, aquí es donde se utiliza onActionViewCollapse () que colapsar el ViewSearch

 @Override public boolean onQueryTextSubmit(String query) { makeSearchRequest(SEARCH_TYPE_KEYWORD, query); searchView.setQuery("", false); searchView.clearFocus(); searchView.onActionViewCollapsed(); buttonClearSearchResults.setVisibility(View.VISIBLE); return false; } 
  • Carga de imagen asíncrona, compruebe si una imagen se recicla
  • Bucle para cargar las imágenes de la lista una por una
  • Filtro ListView con CursorLoader y CursorAdapter personalizado
  • Centrarse en EditText en ListView cuando los descendientes de bloque (Android)
  • Mostrar alertDialog con los botones de radio cuando haga clic en un listViewItem
  • Aumentar dinámicamente el número de elementos en una vista de lista
  • Actividad refrescante al recibir la notificación de gcm push
  • Elemento de vista de lista de Android que no muestra el color seleccionado
  • Cómo traducir el lienzo y seguir recibiendo los eventos táctiles en los lugares correctos
  • Cómo hacer que los elementos de la lista obtengan DragEvents después de desplazarse
  • Android - Visualización de los resultados del cursor de DB en ListView
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.