Android search with Fragments
Solution 1
In short, you can't. There are a couple of reasons why creating a search interface within a Fragment
is not possible.
When creating a searchable interface, you must specify a default "searchable activity" in your Android manifest. As I'm sure you know, a
Fragment
cannot exist without a parentActivity
and thus, this separation is not possible.-
If you already figured out #1 already, I assume you asked this question in hopes that there is some magical "hack" out there that can get the job done. However, the documentation states that,
When the user executes a search in the search dialog or widget, the system starts your searchable activity and delivers it the search query in an Intent with the ACTION_SEARCH action. Your searchable activity retrieves the query from the intent's QUERY extra, then searches your data and presents the results.
The underlying, internal system that is responsible for providing search results expects an
Activity
, not aFragment
; thus, implementing a search interface that is completely independent of anActivity
is not possible, as it would require changes to the underlying system itself. Check out the source code for theSearchableInfo
class if you don't believe me :).
That being said, it doesn't seem like it would be too difficult to achieve something similar to what you are describing. For instance, you might consider implementing your searchable-Activity so that it will accept the android.intent.action.SEARCH
intent and (instead of immediately displaying the results in a ListView
, for example) will pass the search query to your Fragment
s. For instance, consider the following searchable Activity:
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
}
}
When a search-request is made, the system will launch your searchable activity, perform the query, and will pass the results to some container Activity (based on your implementation of doMySearch
). The container Activity will then pass these results to the contained searchable Fragment
, in which the results will be displayed. The implementation requires a bit more work than what you were probably hoping for, but I'm sure there are ways that you can make it more modular, and it seems like this might be the best that you can do.
p.s. If you use this approach, you might have to pay special attention to which Activitys are added/removed to the backstack. See this post for some more information on how this might be done.
p.p.s. You might also forget about the standard search interface completely and just implement a simple search within a Fragment
as described in Raghav's post below.
Solution 2
Here is the example to search something using fragments. Hope it helps and this is what you are looking for:
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);
}
}
}
Solution 3
It is quite possible to search in a fragment using the standard ActionBar SearchView ActionView API. This will work back to Android 2.1 (API level 7) too using AppCompat support classes v7.
In your fragment:
@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;
}
});
}
In your menu 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" />
Solution 4
Using AppCompat support classes v7. Just adding something to @David 's solution from @Rookie solution to get it work properly in a simple manner, here is my fragment code:
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;
}
}
I added the onActivityCreated
, cuz without calling setHasOptionsMenu(true);
the system will not know that this fragment needs to interact with the menu.
then I removed the line inflater.inflate(R.menu.main, menu);
because it doubled the menu items since Activity inflated a menu, then Fragment inflated another menu
Thanks to @David and @Rookie
Solution 5
When working with Fragments
you still need to use an Activity
to control and assign the Fragments
.
This Activity
can have search functionality as before.
I've recently switched from a 'normal' Activity
based app, to a Fragment
based app and the search functionality worked just the same for me.
Have you tried working on it, and didn't succeed? If so give some more detail in your question.
EDIT:
If you want to have a fragment specific search, have all your Fragments
extend an interface MyFragment
with a startSearch
method, and have your Activity
's startSearch
method call the current fragment's startSearch
method.
Related videos on Youtube
Blackbelt
Updated on March 10, 2020Comments
-
Blackbelt about 4 years
Does somebody know of a tutorial or an example of how to implement the standard Android search interface with
Fragment
s? In other words, is it possible to put a standard search with aSearchManager
in a Fragment?-
Snicolas about 12 yearsWho would you grant the bonus to @blackbelt ?Raghav gave the answer I was looking for. but Alex LockWood also answered to the first part of your question.
-
Blackbelt about 12 yearsI accept LockWood's answer. Award the bounty as you like (I think is better)
-
Alex Lockwood about 12 yearsI'm glad we ended up getting such a variety of correct answers and workarounds! :)
-
Rookie almost 12 yearsCan anyone give me the answer to this question..? I am stuck here stackoverflow.com/questions/10600660/…
-
span almost 12 yearsI'm using this technique to stay within the fragment after adding the SearchView to the actionbar: stackoverflow.com/a/6939735/1068167 . My application is not done yet but hopefully it will work.
-
Vinay S Shenoy over 11 yearsI have successfully implemented Search in fragments using the Android Search Interface(SearchView in Action Bar) with custom suggestions and displaying of results. Each fragment loads its own suggestions dynamically from the network(cached into a local DB). It took a lot of work, but it works very well. Add a comment if you would like me to give my answer because it will take a lot of explanation.. :-)
-
Blackbelt over 11 yearsgo ahead. could be useful for other people. thanks
-
-
Blackbelt about 12 yearsRequirement was that the android search was bounded to the specific fragment, not to the activity. So, no I had not tried it.
-
Alex Lockwood about 12 yearsThe OP asked for "standard search interface", but this does achieve a simple "search" within a
Fragment
so I wouldn't complain. Congrats! :P -
Adrian Monk about 12 yearsNext time you might want to cite your sources.
-
Alex Lockwood about 12 yearsI'd also like to point out that Jake Wharton decided not to implement the
SearchView
for his ActionBarSherlock library due to its complexity (see his comment here). As you can see (and as I described in my answer as well), Android's search implementation is anything but trivial... it is ingrained in the underlying system, and this makes even 3rd party libraries difficult to write (and that's saying something... Jake Wharton is like the Chuck Norris of 3rd party libraries! :D). -
Alex Lockwood about 12 yearsTake a look at the
SearchView.java
source code here. -
Alex Lockwood about 12 yearsSee creating a search interface on the developer's site. The OP asked if it was possible to create a "searchable-Fragment" that works with the standard Android search as described in the documentation.
-
Jose_GD almost 12 yearsAlex, it seems Jake Wharton changed his mind: twitter.com/JakeWharton/status/221169921235755009
-
Vinay S Shenoy over 11 years@Alex, It's definitely possible to implement the standard Android search interface using fragments. But it takes a LOT of work... :-)
-
Alex Lockwood over 11 years@VinaySShenoy yeah, I mean you'd have to re-implement the entire
SearchManager
forFragment
s right? (or something like that) -
Vinay S Shenoy over 11 yearsFor delivering Search Results, I've implemented something similar to what you suggested, delivering the intent to the Activity which delivers it to the right fragment if it's visible. The toughest part was repopulating the Suggestions Table on fragment load with the right data to let me handle displaying suggestions as well as to handle search submit and clicking on suggestions. But I've got a nice framework in place for future apps now.. :-)
-
AlikElzin-kilaka about 11 yearsSome pitfalls: 1-Using a customized search icon doesn't work. 2-SearchView needs to have some management code, like for disabling the content, handling back, etc.
-
Kyle Falconer almost 11 yearsI found this to be a better answer: stackoverflow.com/questions/6938952/… This will allow "searching" from within Fragments. True, it's not the official search mechanism provided by Google, but it works just fine.
-
Pelanes about 10 yearsThis works great with the new ActionBarActivity appcompat v7, thanks
-
Clocker almost 10 yearsAre both of these xml files the same?
-
Snicolas almost 10 years@Clocker, looks like they are. Honestly, after 2 years I can't say
-
ImAtWar over 9 yearsWould you need a Search Manager?
-
Shajeel Afzal almost 9 yearsIn my case
onQueryTextChange
method is not being called. -
MBH over 8 yearsreading the accepted answer, i lost hope..the other answers are complex, i read your answer!!! So simple! thats it...thank you so much
-
hornet2319 over 8 yearsthat works well, thanks! Do you have any advices about Recent Query Suggestions implementation?
-
Raymond Lukanta over 8 yearsI think we have to add setHasOptionsMenu(true) in onActivityCreated. As spotted by @MBH.
-
MorZa over 8 years@RaymondLukanta is right - but I added setHasOptionsMenu(true) in the fragment's onCreate method.
-
deadfish over 8 yearsam I the only one who gets doubled result from onQuertTextSubmit?
-
klimat almost 8 yearsIt still depends on
Activity
. You have to settheme
which supportsActionBar
on it. -
Gowthaman M over 6 years
MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); MenuItemCompat.setActionView(item, sv);
this are the lines are deprecated please help me in this case bro -
Bassinator over 6 yearsI haven't tried it yet, but this sounds like EXACTLY what I'm looking for. I have a navigation drawer with fragments that each contain unrelated search features. I don't want to bypass the android search system entirely, so I needed something like this.
-
David over 6 years@GowthamanM Just use MenuItem.SHOW_AS_ACTION.... if you are able the AppCompat is deprecated on later APIs
-
Rod Lima over 5 yearsHey @GowthamanM, use like this:
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItem.SHOW_AS_ACTION_IF_ROOM); item.setActionView(searchView);