Android Application Development Cookbook(Second Edition)
上QQ阅读APP看书,第一时间看更新

Using ListView, GridView, and Adapters

The ListView and GridView are both descendants of ViewGroup, but they are used more like a View since they are data driven. In other words, rather than defining all the possible Views that might fill a ListView (or GridView) at design time, the contents are created dynamically from the data passed to the View. (The layout of the ListItem might be created at design time to control the look of the data during runtime.)

As an example, if you needed to present a list of countries to a user, you could create a LinearLayout and add a button for each country. There are several problems with this approach: determining the countries available, keeping the list of buttons up to date, having enough screen space to fit all the countries, and so on. Otherwise, you could create a list of countries to populate a ListView, which will then create a button for each entry.

We will create an example, using the second approach, to populate a ListView from an array of country names.

Getting ready

Create a new project in Android Studio and call it ListView. The default ActivityMain class extends the Activity class. We will change it to extend the ListActivity class instead. We will then create a simple string list and bind it to the ListView, to derivate the buttons at runtime.

How to do it...

  1. Open the MainActivity.java file and change the base declaration so it will extend ListActivity instead of the Activity class:
    public class MainActivity extends ListActivity {
  2. Change onCreate() so it matches the following:
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      String[] countries = new String[]{"China", "France", "Germany", "India", "Russia", "United Kingdom", "United States"};
    
      ListAdapter countryAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, countries);
      setListAdapter(countryAdapter);
    
        getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, Viewview, int position, long id) {
                String s = ((TextView) view).getText() + " " +position;
                Toast.makeText(getApplicationContext(), s, 
                    Toast.LENGTH_SHORT).show();
            }
        });
    }
  3. Now run the application on an emulator or device to see the populated ListView.

How it works...

We start by creating a simple array of country names, then use that to populate a ListAdapter. In this example, we used an ArrayAdapter when constructing the ListAdapter, but Android has several other adapter types available as well. Such as, if your data is stored in a database, you could use the CursorAdapter. If one of the built-in types doesn't meet your needs, you can always use the CustomAdapter.

We create the adapter with this line of code:

ListAdapter countryAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, countries);

Here, we instantiate the ArrayAdapter using our string array (the last parameter). Notice the android.R.layout.simple_list_item_1 parameter? This defines the layout for the button. Here, we are using one of the layouts as provided by Android, but we could create our own layout and pass our ID instead.

Once we have the adapter ready, we just pass it to the underlying ListView with the setListAdapter() call. (The underlying ListView comes from extending the ListViewActivity.) Finally, we implement the setOnItemClickListener to display a Toast when the user presses a button (which represents a country) in the list.

ListViews are very common in Android as they make efficient use of screen space with a scrolling View, which can be very handy on small screens. The ScrollView layout offers an alternative approach to create a similar scrolling effect. The main difference between the two approaches is that the ScrollView layout is fully inflated before being shown to the user, whereas the ListView only inflates the Views that will be visible. For limited data, this may not be an issue, but for larger data sets, the application could run out of memory before the list is even shown.

Also, since the ListView is driven by a data adapter, the data can easily be changed. Even in our limited example, adding a new country to the screen is as simple as adding the name to the country list. More importantly, the list can be updated during runtime while the user is using the app (for example, downloading an updated list from a website to show real-time options).

There's more...

The ListView also supports a multiple selection mode using the setChoiceMode() method. To see it in action, add the following line of code after setListAdapter():

getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

Then, change the ListItem layout from android.R.layout.simple_list_item_1 to android.R.layout.simple_list_item_checked.

While most applications requiring a scrolling list turn to the ListView, Android also offers the GridView. They are very similar in functionality, even using the same data adapters. The main difference is visual which allows multiple columns. For a better understanding, let's change the ListView example to a GridView.

To start, we need to change MainActivity to extend from Activity again, instead of ListActivity. (This will undo the preceding Step 1.) Then, replace onCreate() with the following code:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GridView gridView = new GridView(this);
    setContentView(gridView);
    String[] countries = new String[]{"China", "France", "Germany", "India", "Russia", "United Kingdom", "United States"};
    ListAdapter countryAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, countries);
    gridView.setAdapter(countryAdapter);
    gridView.setNumColumns(2);
    gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String s = ((TextView) view).getText() + " " + position;
            Toast.makeText(getApplicationContext(), s, Toast.LENGTH_SHORT).show();
        }
    });
}

As you can see, there's more setup code for the GridView than there was for the ListView. The onCreate() method creates a new GridView and passes it in the setContentView() call. (We used this variation of setContentView, as was mentioned in Defining and inflating a layout, instead of creating a layout with just a GridView, but the end result is the same.)

The ListViewActivity base class handles much of this, but the GridView does not have a corresponding activity class to extend.