Búsqueda de Android con Fragmentos
¿Alguien sabe de un tutorial o un ejemplo de cómo implementar la interfaz de búsqueda estándar de Android con Fragment
s? En otras palabras, ¿es posible poner una búsqueda estándar con un SearchManager
en un Fragmento?
- SQlite - Android - Sintaxis de clave extranjera
- La escritura concurrente a la base de datos android (de múltiples servicios)?
- Usando la instrucción update en sqlite en android
- Advertencia: No codifique "/ data /"; Utilice Context.getFilesDir (). GetPath () en su lugar
- SQLite de Android usando db.query () para JOIN en lugar de rawquery ()
- Cómo hacer frente a múltiples cambios en la versión de la base de datos cuando se actualiza la aplicación Android
- La restricción UNIQUE falló: base de datos sqlite: android
- Expandablelistview extiende simplecursoradapter para poblar desde sqlite
- Comprobación del resultado de rawQuery de Android
- Obtenga latitud y longitud de la base de datos SQLite para usarla en Android MapOverlay
- ¿Cómo puedo restablecer un número de secuencia autoincrement en sqlite
- Desarrollo de Android - SQLite storage float
- ¿Cómo puedo obtener información sobre la columna causó restricción de clave externa en android sqlite?
En resumen, no se puede. Hay un par de razones por las que no es posible crear una interfaz de búsqueda dentro de un Fragment
.
-
Al crear una interfaz de búsqueda, debe especificar una "actividad de búsqueda" predeterminada en su manifiesto de Android. Como estoy seguro de que usted sabe, un
Fragment
no puede existir sin unaActivity
padres y por lo tanto, esta separación no es posible. -
Si ya has descubierto # 1 ya, supongo que has hecho esta pregunta con la esperanza de que hay algún "hack" mágico por ahí que puede hacer el trabajo. Sin embargo, la documentación establece que,
Cuando el usuario ejecuta una búsqueda en el cuadro de diálogo de búsqueda o widget, el sistema inicia su actividad de búsqueda y le entrega la consulta de búsqueda en un intento con la acción ACTION_SEARCH. Su actividad de búsqueda recupera la consulta de la intención QUERY extra, luego busca en sus datos y presenta los resultados.
El sistema interno subyacente que es responsable de proporcionar resultados de búsqueda espera una
Activity
, no unFragment
; Por lo tanto, la implementación de una interfaz de búsqueda que es completamente independiente de unaActivity
no es posible, ya que requeriría cambios en el propio sistema subyacente . Compruebe hacia fuera el código de fuente para la clase deSearchableInfo
si usted no me cree :).
Dicho esto, no parece que sería demasiado difícil lograr algo similar a lo que usted está describiendo. Por ejemplo, puede considerar implementar su actividad de búsqueda para que acepte la intención de android.intent.action.SEARCH
y (en lugar de mostrar inmediatamente los resultados en un ListView
, por ejemplo) pasará la consulta de búsqueda a su Fragment
. Por ejemplo, considere la siguiente actividad de búsqueda:
public class SearchableActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); doMySearch(query); } } /** * Performs a search and passes the results to the container * Activity that holds your Fragments. */ public void doMySearch(String query) { // TODO: implement this } }
Cuando se realiza una solicitud de búsqueda, el sistema iniciará su actividad de búsqueda, realizará la consulta y pasará los resultados a alguna actividad de contenedor (basada en su implementación de doMySearch
). La actividad del contenedor pasará estos resultados al Fragment
búsqueda que se puede buscar, en el que se mostrarán los resultados. La implementación requiere un poco más de trabajo de lo que probablemente esperabas, pero estoy seguro de que hay maneras en que puedes hacerlo más modular, y parece que esto podría ser lo mejor que puedes hacer.
Ps Si utiliza este enfoque, puede que tenga que prestar especial atención a las actividades que se agregan o quitan a la pila posterior. Vea este post para obtener más información sobre cómo se puede hacer esto.
Pps También puede olvidarse de la interfaz de búsqueda estándar completamente y sólo implementar una búsqueda simple dentro de un Fragment
como se describe en el post de Raghav a continuación .
Aquí está el ejemplo para buscar algo usando fragmentos. Espero que ayude y esto es lo que buscas:
public class LoaderCursor extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getFragmentManager(); // Create the list fragment and add it as our sole content. if (fm.findFragmentById(android.R.id.content) == null) { CursorLoaderListFragment list = new CursorLoaderListFragment(); fm.beginTransaction().add(android.R.id.content, list).commit(); } } public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. setEmptyText("No phone numbers"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); // Start out with a progress indicator. setListShown(false); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. MenuItem item = menu.add("Search"); item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); SearchView sv = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean onQueryTextChange(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); // The list should now be shown. if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } } }
Es muy posible buscar en un fragmento usando la API ActionView ActionView ActionView estándar. Esto funcionará de nuevo a Android 2.1 (API nivel 7) también utilizando AppCompat clases de soporte v7.
En su fragmento:
@Override public void onCreateOptionsMenu (Menu menu, MenuInflater inflater){ inflater.inflate(R.menu.search, menu); MenuItem item = menu.findItem(R.id.action_search); SearchView sv = new SearchView(((YourActivity) getActivity()).getSupportActionBar().getThemedContext()); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv); sv.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { System.out.println("search query submit"); return false; } @Override public boolean onQueryTextChange(String newText) { System.out.println("tap"); return false; } }); }
En su menú XML
<item android:id="@+id/action_search" android:icon="@drawable/ic_action_search" android:title="Search Waste Items" android:showAsAction="ifRoom|collapseActionView" nz.govt.app:actionViewClass="android.support.v7.widget.SearchView" nz.govt.app:showAsAction="ifRoom|collapseActionView" />
Uso de las clases de soporte de AppCompat v7. Sólo añadiendo algo a la solución de @David de la solución @Rookie para que funcione correctamente de una manera sencilla, aquí está mi código de fragmento:
MyFragment :
public class MyFragment extends Fragment implements SearchView.OnQueryTextListener { @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // What i have added is this setHasOptionsMenu(true); } @Override public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) { //inflater.inflate(R.menu.main, menu); // removed to not double the menu items MenuItem item = menu.findItem(R.id.action_search); SearchView sv = new SearchView(((MainActivity) getActivity()).getSupportActionBar().getThemedContext()); MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv); sv.setOnQueryTextListener(this); sv.setIconifiedByDefault(false); sv.setOnSearchClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Utils.LogDebug("Clicked: "); } }); MenuItemCompat.setOnActionExpandListener(item, new MenuItemCompat.OnActionExpandListener() { @Override public boolean onMenuItemActionCollapse(MenuItem item) { // Do something when collapsed Utils.LogDebug("Closed: "); return true; // Return true to collapse action view } @Override public boolean onMenuItemActionExpand(MenuItem item) { // Do something when expanded Utils.LogDebug("Openeed: "); return true; // Return true to expand action view } }); super.onCreateOptionsMenu(menu,inflater); } @Override public boolean onQueryTextSubmit(String query) { Utils.LogDebug("Submitted: "+query); return true; } @Override public boolean onQueryTextChange(String newText) { Utils.LogDebug("Changed: "+newText); return false; } }
He añadido el onActivityCreated
, cuz sin llamar setHasOptionsMenu(true);
El sistema no sabrá que este fragmento necesita interactuar con el menú.
Entonces quité la línea inflater.inflate(R.menu.main, menu);
Porque dobló los elementos del menú ya que la actividad infló un menú, luego el fragmento infló otro menú
Gracias a @David y @Rookie
Cuando trabaje con Fragments
, todavía necesita usar una Activity
para controlar y asignar los Fragments
. Esta Activity
puede tener la funcionalidad de búsqueda como antes.
Recientemente cambié de una aplicación "normal" basada en Activity
, a una aplicación basada en Fragment
y la funcionalidad de búsqueda funcionó igual para mí.
¿Ha intentado trabajar en él, y no tuvo éxito? Si es así dar más detalles en su pregunta.
EDITAR:
Si desea tener una búsqueda específica de fragmentos, MyFragment
que todos sus Fragments
extiendan una interfaz MyFragment
con un método startSearch
y startSearch
método startSearch
su Activity
startSearch
método startSearch
del fragmento actual.
Creo que lo logré: puedes usar fragmentos y añadir un icono de búsqueda a una barra de acción para que sea posible una búsqueda dentro de los fragmentos. El truco es usar una barra de acción, una vista de acción, un oyente para ella, un cargador y un adaptador por supuesto.
Esto funciona bastante bien, aunque elimina completamente el mecanismo de búsqueda de plataformas Android (pero podría completarse con algún trabajo para encontrar lo que @Alex Lockwood describe y pasar la búsqueda a fragmentos). No reaccionaría a una intención como se esperaba en el caso de una actividad, pero funciona: los usuarios pueden buscar dentro de fragmentos.
Aquí está el código:
SearchInFragmentActivity
package com.sof.test.searchfragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.View; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.Tab; import com.actionbarsherlock.app.ActionBar.TabListener; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.sof.test.searchfragment.SearchFragment; import com.sof.test.R; public class SearchInFragmentActivity extends SherlockFragmentActivity implements TabListener { private SearchFragment tab1 = new SearchFragment(); private SearchFragment tab2 = new SearchFragment(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView( R.layout.search_in_fragments ); getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); createTab( R.string.tab1, R.drawable.ic_menu_search ); createTab( R.string.tab2, R.drawable.ic_menu_search ); getSupportActionBar().setSelectedNavigationItem( 0 ); invalidateOptionsMenu(); } private void createTab(int tabNameResId, int tabIconResId) { ActionBar.Tab tab = getSupportActionBar().newTab(); tab.setText( tabNameResId ); tab.setTabListener(this); getSupportActionBar().addTab(tab); }// met @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { if( ft == null ) { return; }//if View fragmentSlot = findViewById( R.id.fragment ); Fragment newFragment = null; if( fragmentSlot != null ) { newFragment = (tab.getPosition() == 0) ? tab1 : tab2; ft.replace(R.id.fragment, newFragment ); ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_FADE); }//if } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } }//class
La clase de fragmento SearchFragment (uso 2 instancias dentro de la actividad anterior).
package com.sof.test.searchfragment; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.os.Bundle; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.SearchView; import android.widget.TextView; import com.sof.test.R; import com.actionbarsherlock.app.SherlockListFragment; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; public class SearchFragment extends SherlockListFragment { private StringLoader loader = null; private StringAdapter adapter = null; private List<String> listData = new ArrayList<String>(); private String query; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); createListData(); loader = new StringLoader( getActivity(), this ); adapter = new StringAdapter(listData); setListAdapter(adapter); getLoaderManager().initLoader(0, null, new LoaderCallBacks() ); loader.forceLoad(); setHasOptionsMenu( true ); return view; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater ) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate( R.menu.menu_search, menu); System.out.println( "inflating menu"); final SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { showFilteredItems( newText ); return true; } @Override public boolean onQueryTextSubmit(String query) { return true; } }; searchView.setOnQueryTextListener(queryTextListener); return; }//met private void showFilteredItems( String query ) { this.query = query; loader.onContentChanged(); } private void createListData() { for( int i = 0; i < 100 ; i ++ ) { listData.add( "String "+ i ); } } public List<String> getData() { List<String> listFilteredData = new ArrayList<String>(); for( String string : listData ) { if( query == null || string.contains( query ) ) { listFilteredData.add( string ); } } return listFilteredData; }//met private class LoaderCallBacks implements LoaderCallbacks< List<String>> { @Override public void onLoadFinished(Loader<List<String>> loader, List<String> listData) { adapter.setListData( listData ); }// met @Override public void onLoaderReset(Loader<List<String>> listData) { adapter.setListData( new ArrayList<String>() ); }// met @Override public Loader<List<String>> onCreateLoader(int arg0, Bundle arg1) { return loader; }// met }//class private class StringAdapter extends ArrayAdapter< String > { private List<String> listDataToDisplay = new ArrayList<String>(); private LayoutInflater mInflater; public StringAdapter( List<String> listData ) { super( getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, listData ); listDataToDisplay = listData; mInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); }//cons private void setListData( List<String> newListData ) { this.listDataToDisplay.clear(); this.listDataToDisplay.addAll( newListData ); notifyDataSetChanged(); }//met /** * Populate new items in the list. */ @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false); } else { view = convertView; } ((TextView)view.findViewById( android.R.id.text1)).setText( listDataToDisplay.get( position ) ); return view; } }//inner class }//class class StringLoader extends AsyncTaskLoader<List<String>> { SearchFragment fragment = null; public StringLoader(Context context, SearchFragment fragment) { super(context); this.fragment = fragment; }// cons @Override public List<String> loadInBackground() { return fragment.getData(); }// met }// class
El archivo xml para el menú de los fragmentos de búsqueda res / menu / menu_search.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="horizontal" > <FrameLayout android:id="@+id/fragment" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
Y el archivo de diseño xml res / layout / search_in_fragments.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="horizontal" > <FrameLayout android:id="@+id/fragment" android:layout_width="0px" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
Utilice el ActionBar
y ActionBar
. Usted será capaz de manejar las búsquedas sin ninguna conexión con la actividad. Simplemente establezca un OnQueryTextListener
en el SearchView.
MenuItem item = menu.add("Search"); SearchView sv = new SearchView(getActionBar().getThemedContext()); item.setActionView(sv); item.setIcon(R.drawable.ic_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItem.SHOW_AS_ACTION_IF_ROOM); sv.setOnQueryTextListener(new OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { //... return false; } @Override public boolean onQueryTextChange(String newText) { //... return false; } });
Consulte esta publicación para obtener más información sobre la búsqueda personalizada.
Un Fragment
no puede existir fuera de una Activity
, ni un Fragment
puede estar enlazado a un android.intent.action.SEARCH
o cualquier otro intent-filter
.
Así que sin utilizar una Activity
para envolver el Fragment
, lo que está pidiendo no es posible.
En caso de Fragmentos en un ViewPager, podría hacerlo bloqueando el botón de búsqueda cuando no estoy en el fragmento donde quiero dar una barra de búsqueda. En la actividad:
@Override public boolean onSearchRequested() { if (mPager.getCurrentItem() == mAdapter.getPosition(FragmentType.VIDEO)) return super.onSearchRequested(); else return false; }
Y en caso de que no hay ningún botón de búsqueda física, he añadido un elemento de acción en el fragmento, que desencadenan este código:
@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.search_item) { return getSherlockActivity().onSearchRequested(); } return super.onOptionsItemSelected(item); }
si es posible,
Implementa la vista de búsqueda en su actividad, 'onQueryTextChange' de Activity también escuchará la búsqueda en fragmento, puede comprobar la visibilidad del fragmento en 'onQueryTextChange', si es visible puede llamar a su método de búsqueda para el fragmento, su funcionamiento perfecto en mi código
Encontré un trabajo alrededor 🙂 usted puede substituir este método (startActivity (intención)) en su BaseActivity y después comprobar si la acción es ACTION_SEARCH entonces hace su trabajo especial: D
@Override public void startActivity(Intent intent) { try { if (intent.getAction().equals(Intent.ACTION_SEARCH)) toast("hello"); } catch (Exception e) { } }
Otros solución ….. no me gusta eso. Esto es más fácil para mí. Pero es mi idea. Estoy esperando tu opinión
public interface SearchImpl { public void searchQuery(String val); }
Fragmento
public class MyFragment extends Fragment implements SearchImpl { View view; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_almanca, container, false); return view; } @Override public void searchQuery(String val) { Log.e("getted", val); } }
Acitividad
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { Log.e("setted", "" + query); try { MyFragment myFGM=new MyFragment(); myFGM.searchQuery(query); } catch (Exception e) { e.printStackTrace(); } return false; } @Override public boolean onQueryTextChange(String newText) { return false; } }); return super.onCreateOptionsMenu(menu); }
- ¿Por qué se rota una imagen capturada con la intención de la cámara en algunos dispositivos de Android?
- ¿Por qué no usar siempre android: configChanges = "keyboardHidden | orientation"?