Using SearchView

Now that FlickrFetchr supports searching, it is time to add a way for the user to enter a query and initiate a search. Do this by adding a SearchView.

SearchView is an action view – a view that may be included within the toolbar. SearchView allows your entire search interface to live within your application’s toolbar.

First, confirm that a toolbar (containing your app title) appears at the top of your app. If not, follow the steps outlined in Chapter 13 to add a toolbar to your app.

Next, create a new menu XML file for PhotoGalleryFragment in res/menu/fragment_photo_gallery.xml. This file will specify the items that should appear in the toolbar.

Listing 27.6  Adding menu XML file (res/menu/fragment_photo_gallery.xml)

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/menu_item_search"
          android:title="@string/search"
          app:actionViewClass="android.support.v7.widget.SearchView"
          app:showAsAction="ifRoom" />

    <item android:id="@+id/menu_item_clear"
          android:title="@string/clear_search"
          app:showAsAction="never" />
</menu>

You will see a couple of errors in the new XML, complaining that you have not yet defined the strings you are referencing for the android:title attributes. Ignore those for now. You will fix them in a bit.

The first item entry in Listing 27.6 tells the toolbar to display a SearchView by specifying the value android.support.v7.widget.SearchView for the app:actionViewClass attribute. (Notice the usage of the app namespace for the showAsAction and actionViewClass attributes. Refer back to Chapter 13 if you are unsure of why this is used.)

SearchView (android.widget.SearchView) was originally introduced long ago in API 11 (Honeycomb 3.0). However, SearchView was more recently included as part of the support library (android.support.v7.widget.SearchView). So which version of SearchView should you use? You have seen our answer in the code you just entered: the support library version. This may seem strange, as your app’s minimum SDK is 19.

We recommend using the support library for the same reasons outlined in Chapter 7. As features get added with each new release of Android, the features are often back-ported to the support library. A prime example is theming. With the release of API 21 (Lollipop 5.0), the native framework SearchView supports many options for customizing the SearchView’s appearance. The only way to get these fancy features on earlier versions of Android (down to API 7) is to use the support library version of SearchView.

The second item in Listing 27.6 will add a Clear Search option. This option will always display in the overflow menu because you set app:showAsAction to never. Later on you will configure this item so that, when pressed, the user’s stored query will be erased from the disk. For now, you can ignore this item.

Now it is time to address the errors in your menu XML. Open strings.xml and add the missing strings.

Listing 27.7  Adding search strings (res/values/strings.xml)

<resources>
        ...
        <string name="search">Search</string>
        <string name="clear_search">Clear Search</string>

</resources>

Finally, open PhotoGalleryFragment. Add a call to setHasOptionsMenu(true) in onCreate(…) to register the fragment to receive menu callbacks. Override onCreateOptionsMenu(…) and inflate the menu XML file you created. This will add the items listed in your menu XML to the toolbar.

Listing 27.8  Overriding onCreateOptionsMenu(…) (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        setHasOptionsMenu(true);
        new FetchItemsTask().execute();
        ...
    }
    ...
    @Override
    public void onDestroy() {
        ...
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        super.onCreateOptionsMenu(menu, menuInflater);
        menuInflater.inflate(R.menu.fragment_photo_gallery, menu);
    }

    private void setupAdapter() {
        ...
    }
    ...
}

Fire up PhotoGallery and see what the SearchView looks like. Pressing the search icon expands the view to display a text box where you can enter a query (Figure 27.3).

Figure 27.3  SearchView collapsed and expanded

Set of two screenshots show collapsed and expanded views of search. The first screen shows PhotoGallery in Android phone. The second show search bar on top of the screen. The keypad is displayed below.

When the SearchView is expanded, an x icon appears on the right. Pressing the x icon one time clears out what you typed. Pressing the x again collapses the SearchView back to a single search icon.

If you try submitting a query, it will not do anything yet. Not to worry. You will make your SearchView more useful in just a moment.

Responding to SearchView user interactions

When the user submits a query, your app should execute a search against the Flickr web service and refresh the images the user sees with the search results. Fortunately, the SearchView.OnQueryTextListener interface provides a way to receive a callback when a query is submitted.

Update onCreateOptionsMenu(…) to add a SearchView.OnQueryTextListener to your SearchView.

Listing 27.9  Logging SearchView.OnQueryTextListener events (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        super.onCreateOptionsMenu(menu, menuInflater);
        menuInflater.inflate(R.menu.fragment_photo_gallery, menu);

        MenuItem searchItem = menu.findItem(R.id.menu_item_search);
        final SearchView searchView = (SearchView) searchItem.getActionView();

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                Log.d(TAG, "QueryTextSubmit: " + s);
                updateItems();
                return true;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                Log.d(TAG, "QueryTextChange: " + s);
                return false;
            }
        });
    }

    private void updateItems() {
        new FetchItemsTask().execute();
    }
    ...
}

In onCreateOptionsMenu(…), you pull the MenuItem representing the search box from the menu and store it in searchItem. Then you pull the SearchView object from searchItem using the getActionView() method.

Once you have a reference to the SearchView, you are able to set a SearchView.OnQueryTextListener using the setOnQueryTextListener(…) method. You must override two methods in the SearchView.OnQueryTextListener implementation: onQueryTextSubmit(String) and onQueryTextChange(String).

The onQueryTextChange(String) callback is executed any time text in the SearchView text box changes. This means that it is called every time a single character changes. You will not do anything inside this callback for this app except log the input string.

The onQueryTextSubmit(String) callback is executed when the user submits a query. The query the user submitted is passed as input. Returning true signifies to the system that the search request has been handled. This callback is where you will launch a FetchItemsTask to query for new results. (Right now FetchItemsTask still has a hardcoded query. You will refactor FetchItemsTask in a bit so that it uses a submitted query if there is one.)

updateItems() does not seem terribly useful just yet. Later on you will have several places where you need to execute FetchItemsTask. The updateItems() method is a wrapper for doing just that.

As a last bit of cleanup, replace the line that creates and executes a FetchItemsTask with a call to updateItems() in the onCreate(…) method.

Listing 27.10  Cleaning up onCreate(…) (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        setHasOptionsMenu(true);
        new FetchItemsTask().execute();
        updateItems();
        ...
        Log.i(TAG, "Background thread started");
    }
    ...
}

Run your app and submit a query. The search results will still be based on the hardcoded query in Listing 27.5, but you should see the images reload. You should also see log statements reflecting the fact that your SearchView.OnQueryTextListener callback methods have been executed.

Note that if you use the hardware keyboard (e.g., from your laptop) to submit your search query on an emulator, you will see the search executed two times, one after the other. It will look like the images start to load, then load all over again. This is because there is a small bug in SearchView. You can ignore this behavior because it is simply a side effect of using the emulator and will not affect your app when it runs on a real Android device.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.20.238.187