Android search with Fragments
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);
}
}
}
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.