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.
- Cómo intercambiar elementos ListView con animación?
- ¿Se llama OnItemSelectedListener.onNothingSelected en absoluto?
- Android ListView: cómo seleccionar un elemento?
- Cómo obtener la miniatura de vídeo de video de youtube en android lista en android
- Cómo alinear el texto de ListView a la derecha en Android?
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).
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?
- ListView elemento de animación de desplazamiento ("UIKit Dynamics" -como)
- Android personalizado listview muy lento al desplazarse
- Custom Listview scroll esconde el proceso de la barra de progreso y ImageView
- Multiline EditText en los problemas de desplazamiento de RecyclerView
- ListView scrollbarStyle con margen / relleno
- Android ListView con 2 TextViews por artículo
- Android - ¿Cómo implementar un NavigationDrawer parcialmente visible en todo momento?
- Detectar desplazamiento hacia arriba y desplazarse hacia abajo en ListView
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; }
- Cómo utilizar Toast en ASyncTask / onPostExecute con cadena de recursos?
- No se puede generar APK firmado con proguard habilitado cuando se usa Joda Time