Android Actionbar Search widget implementation In ListFragment

22,076

Yes, as a matter of fact, that is the "normal" use case. What you have to remember is that the fragment that uses the search action bar widget, should be the part that registers it in general, since the widget will get destroyed along with that fragment.

Here's how to properly tie it up:

in your ListFragment let it be known that you have option menu items...

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    ..
    setHasOptionsMenu(true);
    ..
}

then inside the same ListFragment create your options menu by overriding the callback, once you have the SearchView widget reference, register a QueryTextListener callback:

@Override 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.grid_default, menu); 
    SearchView searchView = (SearchView)menu.findItem(R.id.grid_default_search).getActionView();
    searchView.setOnQueryTextListener(queryListener);
}

create your search widget via either programmatically (Java) or declaratively (XML) here is XML (named grid_default.xml, from above):

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item     
        android:id="@+id/grid_default_search"
        android:icon="@android:drawable/ic_menu_search"
        android:title="search"
        android:showAsAction="always"
        android:actionViewClass="android.widget.SearchView" 
    />

    <!-- other items or whatever  -->

</menu>

now back in your ListFragment you need to create the queryListener we registered above:

private String grid_currentQuery = null; // holds the current query...

final private OnQueryTextListener queryListener = new OnQueryTextListener() {       

    @Override
    public boolean onQueryTextChange(String newText) {
        if (TextUtils.isEmpty(newText)) {
            getActivity().getActionBar().setSubtitle("List");               
            grid_currentQuery = null;
        } else {
            getActivity().getActionBar().setSubtitle("List - Searching for: " + newText);
            grid_currentQuery = newText;

        }   
        getLoaderManager().restartLoader(0, null, MyListFragment.this); 
        return false;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {            
        Toast.makeText(getActivity(), "Searching for: " + query + "...", Toast.LENGTH_SHORT).show();
        return false;
    }
};

now you know when a query is changed or submitted, in my example, I re-query anytime a key is pressed because my database was small and fast with results, you might need to change this.. but you'll notice how I am just using the widget to set a private class field inside the ListFragment to the current text of the search widget (grid_currentQuery) and then I am just calling getLoaderManager().restartLoader(0, null, MyListFragment.this); where inside my onCreateLoader I am just updating the query I used like this:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bargs) {

    String sort = "SortColumn ASC";
    String[] grid_columns = new String[] { "ColumnA", "ColumnB", "Etc..." };
    String grid_whereClause = "ColumnToSearchBy LIKE ?"

    if (!TextUtils.isEmpty(grid_currentQuery)) {            
        return new CursorLoader(getActivity(), DataProvider.CONTENT_URI, grid_columns, grid_whereClause, new String[] { grid_currentQuery + "%" }, sort);
    }       

    return new CursorLoader(getActivity(), DataProvider.CONTENT_URI, grid_columns, null, null, sort);
}

and that's all you really need to do, I realize this is kind of chopped up but it's like this because there is more stuff that needs to happen, setting the adapter, starting the loader in the first place using getLoaderManager().initLoader(0, null, this); and all the rest of the loader callbacks that need to be handled... but if you are following the best practices set forward by the android folks, (using LoaderManagers in your ListActivitys and ListFragmentss it should be pretty easy for you...

I hope this helps feel free to ask for clarification in the comments if you need to -ck

Share:
22,076
bencallis
Author by

bencallis

iOS Developer living in Glasgow. Studied Mobile Software Engineering at Glasgow University.

Updated on July 09, 2022

Comments

  • bencallis
    bencallis almost 2 years

    I currently have my application set up with a ListFragment on the left and a DetailsFragment on the right (similar to the layout on the tablet below).

    layout

    On the action bar I have a search widget which when a search term is submitted should update the contents of the list (in the ListFragment). I have been following the search guide on android dev (http://developer.android.com/guide/topics/search/search-dialog.html) but I am having problems getting the search to update the list in the same activity.

    I have managed to get some form of implementation working by calling a new activity when the search term is submitted but this makes the back button behave oddly (i.e. you press back and you go to the previous activity with the search widget still displaying the search term).

    I have added the intent lines to the manifest as follows

    <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>
        <meta-data android:name="android.app.searchable"
                   android:resource="@xml/searchable"/>
    

    Is it possible to get the search widget to update the list of the current activity?

  • bencallis
    bencallis about 12 years
    Thank you very much for the detailed explanation. I will try to implement it in the next few days and let you know if I have any problems.
  • bencallis
    bencallis about 12 years
    Thanks I have managed to get it working very informative answer. One thing I was wondering. By default in the application I have 3 tabs each representing a category when search I would like to remove these tabs. That is not a problem, the problem I am having is how do I know when the user has finished searching (gone back) to re add the tabs.
  • ckozl
    ckozl about 12 years
    @bencallis you'd most like want to use setOnCloseListener(SearchView.OnCloseListener listener) on the SearchView...
  • bencallis
    bencallis about 12 years
    I was just on with that but I am having issues. searchView.setOnCloseListener(new OnCloseListener() { @Override public boolean onClose() { Toast.makeText(DealPadActivity.this, "Closed", Toast.LENGTH_SHORT).show(); Log.d("Search", "search closed"); It doesn't seem to get called.
  • ckozl
    ckozl about 12 years
    it only gets called with the "x" is clicked to "close" the search, this works fine for me: searchView.setOnCloseListener(new OnCloseListener() { @Override public boolean onClose() { /* this works perfect your problem is elsewhere*/ return false; } });
  • ckozl
    ckozl about 12 years
    you might want to try an "ActionMode" as it has a clearer sense of being "finished" or "closed"...
  • bencallis
    bencallis about 12 years
    stackoverflow.com/questions/9327826/… seems there is a bug in ICS. How would ActionMode work?
  • bencallis
    bencallis about 12 years
    It seems onCloseListener is working on my tablet running ICS but not on my phone :(
  • Deses
    Deses almost 11 years
    Uhm, my only issue is that I don't know what to put on DataProvider.CONTENT_URI. I'm using a prepopulated sqlite database that I copy to the user folder, so I don't have a class for each table. What should I do?
  • Ashish Dwivedi
    Ashish Dwivedi almost 10 years
    getLoaderManager().restartLoader(0, null, MyListFragment.this); this line give me error, I need to search in a ArrayList<String>, Is it any way to set an adapter to show suggestion on text change
  • Sndn
    Sndn about 9 years
    @ckozl My action bar Search View inflated from fragment shows the soft keyboard on touch but the cursor doesn't appear and the text I type doesn't appear on the search box. Why is that?