Ricerca Android con Frammenti

Qualcuno sa di un tutorial o di un esempio di come implementare l’ interfaccia di ricerca standard di Android con Fragment s? In altre parole, è ansible inserire una ricerca standard con un SearchManager in un frammento?

In breve, non puoi. Ci sono un paio di motivi per cui la creazione di un’interfaccia di ricerca all’interno di un Fragment non è ansible.

  1. Quando crei un’interfaccia ricercabile, devi specificare una “attività di ricerca” predefinita nel tuo manifest Android. Come sono sicuro tu sai, un Fragment non può esistere senza un’attività genitoriale e, quindi, questa separazione non è ansible.

  2. Se già hai già capito # 1, presumo che tu abbia fatto questa domanda nella speranza che ci sia un magico “hack” là fuori che possa portare a termine il lavoro. Tuttavia, la documentazione afferma che,

    Quando l’utente esegue una ricerca nella finestra di dialogo o nel widget di ricerca, il sistema avvia l’attività di ricerca e consegna la query di ricerca in un intento con l’azione ACTION_SEARCH. L’attività di ricerca recupera la query dal QUERY aggiuntivo dell’intento, quindi ricerca i dati e presenta i risultati.

    Il sistema interno sottostante, responsabile della fornitura dei risultati di ricerca, prevede un’attività, non un Fragment ; quindi, non è ansible implementare un’interfaccia di ricerca completamente indipendente da un’attività, poiché richiederebbe modifiche al sistema sottostante stesso . Controlla il codice sorgente per la class SearchableInfo se non mi credi :).

Detto questo, non sembra che sarebbe troppo difficile ottenere qualcosa di simile a ciò che stai descrivendo. Ad esempio, potresti prendere in considerazione l’implementazione della tua attività di ricerca in modo che accetti l’ android.intent.action.SEARCH e (invece di visualizzare immediatamente i risultati in un ListView , ad esempio) passerà la query di ricerca ai Fragment . Ad esempio, considera la seguente attività ricercabile:

 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 } } 

Quando viene effettuata una richiesta di ricerca, il sistema avvia l’attività di ricerca, esegue la query e passa i risultati a qualche attività contenitore (in base all’implementazione di doMySearch ). L’attività contenitore passerà quindi questi risultati al Fragment ricercabile contenuto, in cui verranno visualizzati i risultati. L’implementazione richiede un po ‘più di lavoro di quello che probabilmente speravi, ma sono sicuro che ci sono modi per renderlo più modulare, e sembra che questo potrebbe essere il migliore che tu possa fare.

ps Se si utilizza questo approccio, potrebbe essere necessario prestare particolare attenzione a quali attività vengono aggiunte / rimosse nel backstack. Vedi questo post per ulteriori informazioni su come ciò potrebbe essere fatto.

pps Potresti anche dimenticarti completamente dell’interfaccia di ricerca standard e implementare semplicemente una ricerca all’interno di un Fragment come descritto nel post di Raghav in basso .

Ecco l’esempio per cercare qualcosa usando i frammenti. Spero che aiuti e questo è quello che stai cercando:

 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 { // 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 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 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 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); } } } 

È abbastanza ansible cercare in un frammento usando l’API ActionView ActionBar SearchView standard. Ciò funzionerà anche su Android 2.1 (livello API 7) utilizzando le classi di supporto AppCompat v7.

Nel tuo frammento:

 @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; } }); } 

Nel tuo menu XML

  

Utilizzo delle classi di supporto AppCompat v7. Semplicemente aggiungendo qualcosa alla soluzione di @David dalla soluzione @Rookie per farlo funzionare correttamente in un modo semplice, ecco il mio codice di frammento:

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; } } 

Ho aggiunto onActivityCreated , cuz senza chiamare setHasOptionsMenu(true); il sistema non saprà che questo frammento deve interagire con il menu.

poi ho rimosso la linea inflater.inflate(R.menu.main, menu); perché ha raddoppiato le voci del menu poiché l’attività ha gonfiato un menu, quindi Fragment ha gonfiato un altro menu

Grazie a @David e @Rookie

Quando lavori con Fragments hai ancora bisogno di usare un’attività per controllare e assegnare i Fragments . Questa Activity può avere funzionalità di ricerca come prima.

Di recente sono passato da un’app basata su Activity “normale” a un’applicazione basata su Fragment e la funzionalità di ricerca ha funzionato allo stesso modo per me.

Hai provato a lavorarci e non ci sei riuscito? Se è così, dai qualche dettaglio in più alla tua domanda.

MODIFICARE:

Se desideri avere una ricerca specifica per i frammenti, fai in modo che tutti i Fragments estendano un’interfaccia MyFragment con un metodo startSearch e che il metodo startSearch Activity startSearch richiami il metodo startSearch del frammento corrente.

Penso di averlo raggiunto: puoi effettivamente usare i frammenti e aggiungere un’icona di ricerca a una barra delle azioni in modo che sia ansible una ricerca all’interno dei frammenti. Il trucco è usare una barra di azione, una vista di azione, un listener per esso, un caricatore e un adattatore, naturalmente.

Funziona abbastanza bene anche se ignora completamente il meccanismo di ricerca della piattaforma Android (ma potrebbe essere completato con un po ‘di lavoro per trovare ciò che @Alex Lockwood descrive e passare la ricerca ai frammenti). Non reactjsbbe a un intento come previsto nel caso di un’attività, ma funziona: gli utenti possono cercare all’interno dei frammenti.

Ecco il codice:

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 class di frammenti SearchFragment (io uso 2 istanze all’interno dell’attività sopra).

 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 listData = new ArrayList(); 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 getData() { List listFilteredData = new ArrayList(); for( String string : listData ) { if( query == null || string.contains( query ) ) { listFilteredData.add( string ); } } return listFilteredData; }//met private class LoaderCallBacks implements LoaderCallbacks< List> { @Override public void onLoadFinished(Loader> loader, List listData) { adapter.setListData( listData ); }// met @Override public void onLoaderReset(Loader> listData) { adapter.setListData( new ArrayList() ); }// met @Override public Loader> onCreateLoader(int arg0, Bundle arg1) { return loader; }// met }//class private class StringAdapter extends ArrayAdapter< String > { private List listDataToDisplay = new ArrayList(); private LayoutInflater mInflater; public StringAdapter( List 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 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> { SearchFragment fragment = null; public StringLoader(Context context, SearchFragment fragment) { super(context); this.fragment = fragment; }// cons @Override public List loadInBackground() { return fragment.getData(); }// met }// class 

Il file xml per il menu dei frammenti di ricerca res / menu / menu_search.xml:

    

E il file di layout xml res / layout / search_in_fragments.xml

    

Utilizzare ActionBar e SearchView . Sarai in grado di gestire le ricerche senza alcuna connessione all’Attività. Basta impostare un OnQueryTextListener per 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; } }); 

Vedi questo post per maggiori dettagli sulla ricerca personalizzata.

Un Fragment non può esistere al di fuori di un’attività, né un Fragment può essere collegato a un android.intent.action.SEARCH o qualsiasi altro intent-filter .

Quindi, senza usare un’attività per avvolgere il Fragment , ciò che stai chiedendo non è ansible.

In caso di frammenti in un ViewPager, potrei farlo bloccando il pulsante di ricerca quando non sono sul frammento dove voglio dare una barra di ricerca. Nell’attività:

 @Override public boolean onSearchRequested() { if (mPager.getCurrentItem() == mAdapter.getPosition(FragmentType.VIDEO)) return super.onSearchRequested(); else return false; } 

E in caso di nessun pulsante di ricerca fisico, ho aggiunto un elemento azione nel frammento, che triggers questo codice:

 @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.search_item) { return getSherlockActivity().onSearchRequested(); } return super.onOptionsItemSelected(item); } 

ho trovato un modo per aggirare 🙂 è ansible sovrascrivere questo metodo (startActivity (Intent)) nella BaseActivity e quindi verificare se l’azione è ACTION_SEARCH, quindi eseguire il proprio lavoro speciale: D

 @Override public void startActivity(Intent intent) { try { if (intent.getAction().equals(Intent.ACTION_SEARCH)) toast("hello"); } catch (Exception e) { } } 

altre soluzioni ….. non mi piace. Questo è più facile per me. Ma è la mia idea.Im aspettare la tua opinione

 public interface SearchImpl { public void searchQuery(String val); } 

Frammento

 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); } } 

Acitivty

  @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); } 

si è ansible,

per favore implementa la vista di ricerca sulla tua attività, ‘onQueryTextChange’ in Activity ascolterà anche la ricerca in frammento, puoi controllare la visibilità del frammento in ‘onQueryTextChange’, se è visibile puoi chiamare il tuo metodo di ricerca per frammento, funziona perfettamente nel mio codice