Android: ¿Proporciona sugerencias de búsqueda recientes sin actividad de búsqueda?
Tengo un ActionBar SearchView y soy capaz de hacer búsquedas con él. La documentación de Android no explica cómo implementar sugerencias de búsqueda. No quiero tener una actividad de búsqueda.
Este es mi código de búsqueda:
- ¿Cómo hacer una devolución de llamada después de que la vista esté completamente renderizada?
- Relación de aspecto fija
- ¿Cómo puedo establecer dinámicamente la posición de la vista en Android?
- SearchView edittext siempre es nulo
- ¿Es posible desactivar el HeaderLayout que se puede hacer clic en NavigationView?
public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_add_song, menu); final SearchView searchView = (SearchView) menu.findItem(R.id.song_search).getActionView(); searchView.setFocusable(true); searchView.setIconified(false); final AddSongActivity activity = this; searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { // Do nothing return true; } @Override public boolean onQueryTextSubmit(String query) { // Clear SearchView searchView.clearFocus(); // Begin Spotify Search TextView notice = (TextView)findViewById(R.id.search_notice); URL url; try { url = new URL("http://ws.spotify.com/search/1/track.json?q=" + URLEncoder.encode(query,"UTF-8")); } catch (MalformedURLException e) { notice.setText("Malformed Search"); notice.setHeight(noticeHeight); return true; } catch (UnsupportedEncodingException e) { notice.setText("Unsupported Encoding. Maybe a problem with your device."); notice.setHeight(noticeHeight); return true; } new SearchDownload(url, activity).execute(); notice.setText("Loading Tracks"); notice.setHeight(noticeHeight); Log.i("infodb","" + noticeHeight); return true; } });
Esto funciona para la búsqueda, pero no tengo idea de implementar sugerencias recientes de búsqueda. ¿Cómo voy a hacer esto?
Gracias.
- Gallery ImageAdapter convertView siempre es nulo
- ¿Cuál es la forma más rápida de agregar varias vistas a un LinearLayout?
- El método getDrawingCache () de Android 2.1 View devuelve siempre null
- Android, programatically diseño de una vista de botón?
- ¿Qué hace android: isScrollContainer hacer?
- Problema: el niño especificado ya tiene un padre. Debe llamar a removeView () en el padre del niño primero
- Convertir vista a mapa de bits en Android
- Tamaño del artículo de menú personalizado y tamaño de texto en la vista de navegación
Ok, pasé mi tiempo para esto. Hago mi propia implementación de sugerencia simple desde SQLiteDatabase
.
Vamos a crear 3 clases como las siguientes
-
MainActivity
– para la prueba de la sugerencia deSearchView
de la base de datos -
SuggestionDatabase
– esto almacenará su palabra clave de búsqueda reciente. -
SuggestionSimpleCursorAdapter
– esto es una subclase deSimpleCursorAdapter
. Voy a explicar por qué hago esta clase en lugar de usarSimpleCursorAdapter
.
Los códigos
// MainActivity.java public class MainActivity extends Activity implements SearchView.OnQueryTextListener, SearchView.OnSuggestionListener { private SuggestionsDatabase database; private SearchView searchView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); database = new SuggestionsDatabase(this); searchView = (SearchView) findViewById(R.id.searchView1); searchView.setOnQueryTextListener(this); searchView.setOnSuggestionListener(this); } @Override public boolean onSuggestionSelect(int position) { return false; } @Override public boolean onSuggestionClick(int position) { SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position); int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION); searchView.setQuery(cursor.getString(indexColumnSuggestion), false); return true; } @Override public boolean onQueryTextSubmit(String query) { long result = database.insertSuggestion(query); return result != -1; } @Override public boolean onQueryTextChange(String newText) { Cursor cursor = database.getSuggestions(newText); if(cursor.getCount() != 0) { String[] columns = new String[] {SuggestionsDatabase.FIELD_SUGGESTION }; int[] columnTextId = new int[] { android.R.id.text1}; SuggestionSimpleCursorAdapter simple = new SuggestionSimpleCursorAdapter(getBaseContext(), android.R.layout.simple_list_item_1, cursor, columns , columnTextId , 0); searchView.setSuggestionsAdapter(simple); return true; } else { return false; } } }
Cómo funciona
- Cuando el usuario pulsa el botón de búsqueda, se
onQueryTextSubmit()
y luego se guardará la palabra clave de búsqueda en nuestra base de datos. Digamos que enviamos una palabra clave "Hola" - Si el usuario escribe una cadena por ejemplo "Hel" o "H" en
onQueryTextChange()
y luego buscaremos esta palabra clave enSQLiteDatabase
(SuggestionDatabase
). Si "Hel" o "H" coincide con "Hola", muestre los resultados de la consulta estableciendo el Cursor devuelto enSuggestionSimpleCursorAdapter
y luego configure este adaptador enSearchView
. Aquí está la imagen.
3. Por supuesto vamos a tocar la sugerencia que es "Hola", onSuggestionClick(int position)
se llamará para eso. Conseguimos el objeto de SQLiteCursor
del SQLiteCursor
( SuggestionSimpleCursorAdapter
) y conseguimos el texto de la sugerencia de él, fijamos el texto de la sugerencia en el objeto de SearchView
SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position); int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION); searchView.setQuery(cursor.getString(indexColumnSuggestion), false);
Si usamos SimpleCursorAdapter
también funciona correctamente, pero digamos que tenemos este escenario
- Si ejecutamos este programa en smartphone y escribimos la palabra clave "Hel", la sugerencia aparecerá correctamente.
- ¿Qué pasa si giramos la pantalla en el paisaje? Cambiará en modo de pantalla completa y todavía puede escribir la palabra clave.
¿Qué pasará en la sugerencia? Vamos a ver.
¿Ves la extraña sugerencia? ¿Cómo resolvemos eso? convertToString(Cursor cursor)
el convertToString(Cursor cursor)
que devuelve un CharSequence
// SuggestionSimpleCursorAdapter.java public class SuggestionSimpleCursorAdapter extends SimpleCursorAdapter { public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to); } public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { super(context, layout, c, from, to, flags); } @Override public CharSequence convertToString(Cursor cursor) { int indexColumnSuggestion = cursor.getColumnIndex(SuggestionsDatabase.FIELD_SUGGESTION); return cursor.getString(indexColumnSuggestion); } }
Al superar convertToString(Cursor cursor)
, aquí está el resultado
Y aquí está la base de datos
// SuggestionDatabase.java public class SuggestionsDatabase { public static final String DB_SUGGESTION = "SUGGESTION_DB"; public final static String TABLE_SUGGESTION = "SUGGESTION_TB"; public final static String FIELD_ID = "_id"; public final static String FIELD_SUGGESTION = "suggestion"; private SQLiteDatabase db; private Helper helper; public SuggestionsDatabase(Context context) { helper = new Helper(context, DB_SUGGESTION, null, 1); db = helper.getWritableDatabase(); } public long insertSuggestion(String text) { ContentValues values = new ContentValues(); values.put(FIELD_SUGGESTION, text); return db.insert(TABLE_SUGGESTION, null, values); } public Cursor getSuggestions(String text) { return db.query(TABLE_SUGGESTION, new String[] {FIELD_ID, FIELD_SUGGESTION}, FIELD_SUGGESTION+" LIKE '"+ text +"%'", null, null, null, null); } private class Helper extends SQLiteOpenHelper { public Helper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE "+TABLE_SUGGESTION+" ("+ FIELD_ID+" integer primary key autoincrement, "+FIELD_SUGGESTION+" text);"); Log.d("SUGGESTION", "DB CREATED"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } }
Espero que esta respuesta sea útil para otros programadores mucho. 🙂
En la clase MainActivity
de arriba en el onCreate method
use este código primero
AutoCompleteTextView search_text = (AutoCompleteTextView) searchView.findViewById(searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null)); search_text.setThreshold(1);
Este setThreshold(1)
significa que puede buscar el texto de un carácter así ahora.
Mi necesidad era aún más simplista: no necesitaba una base de datos, ya que tenía las sugerencias que muchos querían mostrar contenidas en un ArrayList.
He aquí un ejemplo de implementación:
import java.util.ArrayList; import android.app.Activity; import android.app.SearchManager; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.SearchView; import android.widget.SearchView.OnQueryTextListener; import android.widget.TextView; import android.widget.Toast; public class ActivityTest extends Activity implements OnQueryTextListener { private static final String COLUMN_ID = "_id"; private static final String COLUMN_TERM = "term"; private static final String DEFAULT = "default"; private SearchManager searchManager; private SearchView searchView; private MenuItem searchMenuItem; private SuggestAdapter suggestionsAdapter; private final ArrayList<String> suggestionsArray = new ArrayList<String>(); private final ArrayList<String> dummyArray = new ArrayList<String>(); @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create some dummy entries dummyArray.add("apples"); dummyArray.add("oranges"); dummyArray.add("bananas"); dummyArray.add("pears"); dummyArray.add("plums"); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.main, menu); searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); searchMenuItem = menu.findItem(R.id.action_search); searchView = (SearchView) searchMenuItem.getActionView(); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setOnQueryTextListener(this); final MatrixCursor matrixCursor = getCursor(suggestionsArray); suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray); searchView.setSuggestionsAdapter(suggestionsAdapter); suggestionsAdapter.notifyDataSetChanged(); return true; } @Override public boolean onQueryTextChange(final String newText) { suggestionsArray.clear(); for (int i = 0; i < dummyArray.size(); i++) { if (dummyArray.get(i).contains(newText)) { suggestionsArray.add(dummyArray.get(i)); } } final MatrixCursor matrixCursor = getCursor(suggestionsArray); suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray); searchView.setSuggestionsAdapter(suggestionsAdapter); suggestionsAdapter.notifyDataSetChanged(); return true; } @Override public boolean onQueryTextSubmit(final String query) { // TODO Auto-generated method stub return false; } private class SuggestAdapter extends CursorAdapter implements OnClickListener { private final ArrayList<String> mObjects; private final LayoutInflater mInflater; private TextView tvSearchTerm; public SuggestAdapter(final Context ctx, final Cursor cursor, final ArrayList<String> mObjects) { super(ctx, cursor, 0); this.mObjects = mObjects; this.mInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View newView(final Context ctx, final Cursor cursor, final ViewGroup parent) { final View view = mInflater.inflate(R.layout.list_item_search, parent, false); tvSearchTerm = (TextView) view.findViewById(R.id.tvSearchTerm); return view; } @Override public void bindView(final View view, final Context ctx, final Cursor cursor) { tvSearchTerm = (TextView) view.findViewById(R.id.tvSearchTerm); final int position = cursor.getPosition(); if (cursorInBounds(position)) { final String term = mObjects.get(position); tvSearchTerm.setText(term); view.setTag(position); view.setOnClickListener(this); } else { // Something went wrong } } private boolean cursorInBounds(final int position) { return position < mObjects.size(); } @Override public void onClick(final View view) { final int position = (Integer) view.getTag(); if (cursorInBounds(position)) { final String selected = mObjects.get(position); Toast.makeText(getApplicationContext(), selected, Toast.LENGTH_SHORT).show(); // Do something } else { // Something went wrong } } } private MatrixCursor getCursor(final ArrayList<String> suggestions) { final String[] columns = new String[] { COLUMN_ID, COLUMN_TERM }; final Object[] object = new Object[] { 0, DEFAULT }; final MatrixCursor matrixCursor = new MatrixCursor(columns); for (int i = 0; i < suggestions.size(); i++) { object[0] = i; object[1] = suggestions.get(i); matrixCursor.addRow(object); } return matrixCursor; } }
En mi código actual, tengo una interfaz personalizada que rellena ArrayList con términos dinámicos extraídos de un servidor. Actualizar el conjunto de datos de esta manera:
@Override public void onDataReceived(final ArrayList<String> results) { suggestionsArray.clear(); suggestionsArray.addAll(results); final MatrixCursor matrixCursor = getCursor(suggestionsArray); suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray); searchView.setSuggestionsAdapter(suggestionsAdapter); suggestionsAdapter.notifyDataSetChanged(); }
He encontrado que no inicializar un cursor vacío o volver a crear cada vez que causó problemas.
Espero eso ayude.
Hay un problema que observo en el enfoque anterior.
Cuando el usuario introduce un solo carácter (por ejemplo, "H"
), después de obtener las entradas de la base de datos y establecer el adaptador en searchView a través de searchView.setSuggestionsAdapter(<adapter>)
, no se muestra la lista desplegable.
Sólo después de introducir un segundo carácter (por ejemplo, " ", "a"
), la lista de sugerencias se muestra. ¿Alguien está observando este comportamiento?
- Dagger 2, a veces en la compilación me sale "no se puede encontrar la clase de símbolo DaggerApplicationComponent"
- ¿Por qué podría Resources.getString () devolver intermitentemente cadenas de la configuración regional equivocada?