GridView: plug an Adapter into an AdapterView

Tap on a photo to bring up a piece of Toast containing a bigger version of the photo. The photos in the GridView, and the spacing between them, are the same size (measured in inches) on each device.

The first two screenshots are on a Samsung Galaxy 5S in portrait orientation. Its dimensions are 1080 × 1920 pixels at 480 pixels per inch, so the screen width is 1080/480 inches = 2¼ inches = 160 * 2¼ dp = 360 dp. But the GridView is contained in a RelativeLayout that has a total of 32 dp of padding on its left and right (see values/dimens.xml), leaving us with only 360 − 32 = 328 dp. Each column is 60 dp wide, and the spacing bewteen the columns is 10 dp wide, so we have room for four (but not five) columns:
60 + 10 + 60 + 10 + 60 + 10 + 60 = 270 ≤ 328.

The third screenshot is on an Azpen A727 tablet in landscape orientation. Its dimensions are 800 × 480 pixels at 120 pixels per inch, so the screen width is 800/120 inches = 6⅔ inches = 160 * 6⅔ dp = 1066⅔ dp. But the GridView is contained in a RelativeLayout that has a total of 128 dp of padding on its left and right (see values-w820dp/dimens.xml), leaving us with only 1066⅔ − 128 = 938⅔ dp. Each column is 60 dp wide, and the spacing bewteen the columns is 10 dp wide, so we have room for 13 (but not 14) columns:
60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 + 10 + 60 = 900 ≤ 938⅔

In Spinner and ListView, our Adapter was an off-the-shelf ArrayAdapter. An ArrayAdapter converts each data item to a String (with the Java method toString) and puts the result in a TextView. But the data items in this app are images, so we will have to use a different class of Adapter. In fact, we will have to write our own class of Adapter.

The class will be called ImageAdapter. The most important methods of the adapter are getCount, which returns a number, and getView, which returns a View. In this app, the View is an ImageView. Class ImageView is a subclass of View specialized for displaying an image, just as a TextView is specialized for displaying text. The getView method receives a position argument in the range 0 to 99 inclusive. How can it create 100 ImageViews when we have only eight photos? We put sample_0.jpg into ImageView number 0, 8, 16, 24, 32, etc. We put sample_1.jpg into ImageView number 1, 9, 17, 25, 33, etc. We put sample_2.jpg into ImageView number 2, 10, 18, 26, 34, etc. Et cetera.

getCount and getView are called by the AdapterView. In this app, the AdapterView is a GridView. See Grid View.

One method that is not called is getItem, but I wrote it anyway just for completeness. getItem calls the recently deprecated one-argument getDrawable rather than the newfangled two-argument getDrawable, because I want my app to be able to run on older Android devices. See the minSdkVersion in build.gradle.

In addition to plugging an Adapter into the GridView, we also plug an AdapterView.OnItemClickListener into the GridView to make something happen when an image is clicked. As usual, the “something” will be to pop up a Toast. Until now, our Toasts have contained only text. This time, the Toast will contain an ImageView.

Source code in GridView.zip

  1. MainActivity.java
  2. ImageAdapter.java
  3. R.java creates the int variables R.drawable.sample_0, R.drawable.sample_1, etc.
  4. activity_main.xml. The RelativeLayout that occupies the whole screen has been replaced by a GridView that usually occupies the whole screen. dp are density-independent pixels, 160 per inch.
  5. values/dimens.xml
  6. values-w820dp/dimens.xml. In landcape oriention, the screen width of my Azpen A727 tablet is 800 pixels = 800/120 inches = 6⅔ inches = 160 * 6⅔ dp = 1066⅔ dp.
  7. AndroidManifest.xml
  8. build.gradle (Module: app)
  9. sample_0.jpg (213 × 285)
  10. sample_1.jpg (285 × 213)
  11. sample_2.jpg (285 × 191)
  12. sample_3.jpg (285 × 213)
  13. sample_4.jpg (285 × 190)
  14. sample_5.jpg (191 × 285)
  15. sample_6.jpg (285 × 213)
  16. sample_7.jpg (213 × 285)
  17. sample_thumb_0.jpg (45 × 60). These thumbnails are not used. After I added them to the project, I discovered they were too small for the contemporary number of dpi.
  18. sample_thumb_1.jpg (60 × 45)
  19. sample_thumb_2.jpg (60 × 40)
  20. sample_thumb_3.jpg (60 × 45)
  21. sample_thumb_4.jpg (60 × 40)
  22. sample_thumb_5.jpg (40 × 60)
  23. sample_thumb_6.jpg (60 × 45)
  24. sample_thumb_7.jpg (45 × 60)

Create the project

Create class ImageAdapter

In the Android Studio project view, select the folder app/java/edu.nyu.scps.gridview.
File → New… → Java Class
Create New Class
Name: ImageAdapter
Kind: Class
OK

In the new file ImageAdapter.java, click on the word ImageAdapter and pull down
Code → Implement Methods…
Select the four methods that need to be implemented and press OK.

Add the .jpg fles to the project

In the Macintosh Finder, control-click on each .jpg file and select Copy. In the Android Studio project view, highlight the folder app/res/drawable, control-click on it, and select Paste.

GridView vs. TableLayout

A GridView keeps its Views in the order that you determine, but it does not necessarily keep a View in the same row and column. For example, the number of columns can change when you switch devices (see the above screenshots) or when you change the orientation of a device. Use a TableLayout when you want to keep each View in the same row and column. Use a GridView when you don’t care about keeping a View in the same row and column.

Every row in a GridView contains the same number of Views, except possibly the last. But each row of a TableLayout can have a different number of Views.

TableLayout in ApiDemos:
Views → Layouts → TableLayout

GridView in ApiDemos

  1. Views → Grid → 1. Icon Grid
    Displays a GridView of all the icons of all the apps on the Android device. See action.MAIN and category.LAUNCHER in the AndroidManifest.xml file.
    1. ApiDemos/src/com/example/android/apis/view/Grid1.java
      creates a subclass of BaseAdapter named Grid1.AppsAdapter that puts each icon into an ImageView.
    2. ApiDemos/res/layout/grid_1.xml
      creates a GridView.

  2. Views → Grid → 2. Photo Grid
    Displays a GridView of small photos of dogs.
    1. ApiDemos/src/com/example/android/apis/view/Grid2.java
      creates a subclass of BaseAdapter named Grid2.ImageAdapter that puts each photo into an ImageView. The photos come from resources.
    2. ApiDemos/res/layout/grid_2.xml
      creates a GridView.
    3. ApiDemos/res/drawable/sample_thumb_0.jpg (45 × 60)
    4. ApiDemos/res/drawable/sample_thumb_1.jpg (60 × 45)
    5. ApiDemos/res/drawable/sample_thumb_2.jpg (60 × 40)
    6. ApiDemos/res/drawable/sample_thumb_3.jpg (60 × 45)
    7. ApiDemos/res/drawable/sample_thumb_4.jpg (60 × 40)
    8. ApiDemos/res/drawable/sample_thumb_5.jpg (40 × 60)
    9. ApiDemos/res/drawable/sample_thumb_6.jpg (60 × 45)
    10. ApiDemos/res/drawable/sample_thumb_7.jpg (45 × 60)

  3. Views → Grid → 3. Selection Mode
    Displays a GridView of all the icons of all the apps on the Android device. Hold down an icon for a second to select it; the tap other icons to select them.
    1. ApiDemos/src/com/example/android/apis/view/Grid3.java
      creates a subclass of BaseAdapter named Grid1.AppsAdapter that puts each icon into an ImageView. Also plugs a AbsListView.MultiChoiceModeListener into the GridView.
    2. ApiDemos/res/layout/grid_1.xml
      creates a GridView.
    3. ApiDemos/res/values/colors.xml
      contains a drawable resource named blue.

Things to try

  1. Superimpose green lettering over the ImageView in the Toast. The ImageView and TextView are contained in the simplest type of layout, a FrameLayout. After creating the ImageView, insert the following code.
                    TextView textView = new TextView(MainActivity.this);
                    textView.setBackgroundColor(Color.TRANSPARENT);
                    textView.setTextColor(Color.GREEN);
                    textView.setText(" position = " + position + ", id = " + id);
    
                    FrameLayout frameLayout = new FrameLayout(MainActivity.this);
                    frameLayout.addView(imageView);
                    frameLayout.addView(textView);
    
                    //Insert the FrameLayoutToast.
                    Toast toast = new Toast(MainActivity.this);
                    toast.setView(frameLayout);
                    toast.show();