Three Variations for Image Square Grids in Android

In a new Android app I am working on, I want to display a a square grid of images. I have tried  several variations of doing this using a GridView. Each of the solutions involves using a ViewTreeObserver, which is something I would not have thought of on my own. Fortunately, I found several notes on StackOverflow that pointed me in the right direction.

I’ll describe three of the variations in this blog post. The end result for each variation is good: square images on a grid. My favorite variation is the one in which the sizes of the images are determined dynamically, based on the size of the screen. It requires the least amount of  work and delivers good results.

The other two variations give you more control over the sizes of the image squares. You can choose one size for a 480 x 320 screen, another size for 800 x 480, and still another for a 1024 x 768 tablet. It relies on using resource folders with size qualifiers (examples: values-large, values-xlarge, values-sw600dp, etc.).

The main activity for the application allows you to choose the variation you want.

Variations 1-2

The first variation allows you to specify the size of the images and the containing grid. Its layout definition looks like this.

<GridView xmlns:android=""
 android:verticalSpacing="@dimen/image_thumbnail_spacing" >

Notice that it refers to the following values:

  • image_thumbnail_size
  • image_thumbnail_spacing
  • image_grid_width
  • grid_num_columns

All of those are defined in the values resource folder inside dimens.xml.  To support the different sizes of screens, there are many of these dimens.xml files. Here is one of them. This one would work well for  480 x 320 screen.

 <integer name="grid_num_rows">10</integer>
 <integer name="grid_num_columns">10</integer>
 <dimen name="image_thumbnail_size">30dp</dimen>
 <dimen name="image_thumbnail_spacing">1dp</dimen>
 <dimen name="image_grid_width">320dp</dimen>

The layout file and the values for the dimensions is only half of the solution. When you use the standard GridView, you have to live with the standard GridView behavior, or find a way to work around it. A standard GridView is designed to fill up the entire width of its parent with however many columns you specify. There are lots of variations of parameters you can try, but I have not found one where you end up with the image height matching the image width.

Thanks to several people who provided answers on StackOverflow and a wonderful example on the Android Developers’ website (see References section below), I learned how to use a ViewTreeObserver to adjust the sizes of the images so the height matches the width being used in the grid. All the details of setting up the GirdView with its ImageAdapter can be seen in the full source code (see below). The most important thing is to understand the following code section that appears in the activity’s onCreate method.

// This listener is used to get the final width of the GridView.
// The column width is used to set the height
// of each view so we get nice square thumbnails.
 new ViewTreeObserver.OnGlobalLayoutListener() {
 @Override public void onGlobalLayout() {
   if (mAdapter.getNumColumns() == 0) {
      final int numColumns = (int) Math.floor(
         mGridView.getWidth() / (mImageThumbSize + mImageThumbSpacing));
      if (numColumns > 0) {
         final int columnWidth =
             (mGridView.getWidth() / numColumns) - mImageThumbSpacing;

By the time this code runs, the width of the GridView is known. That allows the width of the images in the grid to be calculated, which is then passed along in the call to the setItemHeight method.

public void setItemHeight(int height) {
  if (height == mItemHeight) {
   mItemHeight = height;
   mImageViewLayoutParams = new GridView.LayoutParams(mItemHeight, mItemHeight);

This method is defined inside the ImageAdapter that works to fill up the GridView. That explains why it looks so simple. You don’t see code to go through all the image views on the grid to adjust their size. What you see is changing the item height and a redefinition of some LayoutParams. The last line is a call to “notifyDataSetChanged”. That causes the adapter to redo all of its work, which includes all of the calls to its getView method. Inside that is where the image views are set up with the dimensions they should be to be squares. Check that code and you will see where it uses mImageLayoutParams.

I said earlier that there would be many of the dimens.xml files, based on an assumption that you always want the square grid to fill the screen. That might not be the case for all apps. If you wanted to have a grid that was always the same size (e.g. 320 x 320), no matter what size the screen is, you could use a definition like the one above.

To handle the landscape orientation, a second dimens.xml is defined in the values-land folder.

 <dimen name="image_thumbnail_size">26dp</dimen>
 <dimen name="image_thumbnail_spacing">1dp</dimen>
 <dimen name="image_grid_width">270dp</dimen>

Note that it uses a slightly smaller grid size because it accounts for the size of the action bar  (or title bar) at the top.

My second variation came about because I wanted to see if I could simplify things by not having to define as many values. The xml for variation 2 does not include the image_grid_width. Because of that the GridView fills the entire width of the screen, which works just fine in portrait orientation, but not so good in landscape.

The images themselves are still squares, but the overall grid is not. I left this variation in this demo app because there might be some situations where expanding to fill the full width is the right thing to do.

Variation 3 – Dynamic Image Sizes

The variation I like the best is the one where the image sizes and the grid size are determined dynamically. Other than specifying the number of columns and the spacing, you do not have to do anything to end up with square images within a square grid.

Its layout file is not as complicated.

<GridView xmlns:android=""
 android:verticalSpacing="@dimen/image_thumbnail_spacing" >

This solution builds on the earlier ones. It still depends on there being a ViewTreeObserver. From the other variations, I understood what it could be used for. So I changed it so it would do more that adjust the height of the image views in the grid. Since the grid view height and width are known, it recalculates that view and all the image views contained in it.

 new ViewTreeObserver.OnGlobalLayoutListener() {
 public void onGlobalLayout() {
   // When we get here, the size of the frame view is known.
   // Use those dimensions to set the width of the columns 
   // and the image adapter size.
   if (mAdapter.getNumColumns() == 0) {
     View f = mFrameView;
     int fh = f.getHeight () 
              - f.getPaddingTop () - f.getPaddingBottom ();
     int shortestWidth = fh;
     int fw = f.getWidth () - f.getPaddingLeft () - f.getPaddingRight ();
     if (fw < shortestWidth) shortestWidth = fw;
       int usableWidth = shortestWidth 
                         - (0 + mNumColumns) * mImageThumbSpacing
                         - f.getPaddingLeft () - f.getPaddingRight ();
       usableWidth = shortestWidth - (0 + mNumColumns) * mImageThumbSpacing;
       int columnWidth = usableWidth / mNumColumns;
       mImageThumbSize = columnWidth;
       int gridWidth = shortestWidth;
       // The columnWidth used is an integer. That means that we have a
       // little unused space. Fix that up with padding if the unused 
       // space is more than half of an image.
       int estGridWidth = mNumColumns * columnWidth;
       int unusedSpace = (shortestWidth - estGridWidth);
       boolean addPadding = (unusedSpace * 2) > mImageThumbSize;
       if (addPadding) {
          // This is not a precise calculation. Pad with roughly 1/3 the unused space.
          int pad = unusedSpace / 3;
          if (pad > 0) mGridView.setPadding (pad, pad, pad, pad);
       mGridView.setColumnWidth (columnWidth);
       // Now that we have made all the extra adjustments, resize the grid
       // and have it redo its view one more time.
       LayoutParams lparams = new LinearLayout.LayoutParams (gridWidth, gridWidth);
       mGridView.setLayoutParams (lparams);

There is a lot going on in the code above. The key points are these:

  • The amount of space available for a GridView is known.
  • The shortest width can be determined and the grid can be sized for that value.
  • With a new shortest width, a new value for column width can be calculated.
  • Forcing the grid to redo all of its image views results in the square images needed.

This variation works everywhere that I have tried it. That includes tablets (like Xoom and Kindle Fire) and large and small screens on phones.

Source Code

You can download the source code form the website. Click here: download sources from The zip is attached to the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.0.3 (API 15). It works in all API levels from API 8 and on.


ViewTreeObserver – Using this class with your activity makes it possible to get the information you need to size your images and GridView to have a grid of squares.

How do you retrieve dimensions of a view? – article on Stack Overflow that describes the ViewTreeObserver onGlobalLayout method and how to use it to get the size.

Getting Layout Dimensions in Android– More discussion about getting the width of a view after layout.

Displaying Bitmaps Efficiently – Not only is this a great reference about displaying images, it has the best example I found for adjusting the height of image views in a grid. All the onGlobalLayout handlers I did are based on this example. Once again, we should all thank the writers for the Android Developers’ website.

My Dashboard Interface on Tablets – This is a blog article I did about being able to have an app adapt easily to all the different size screens on devices and tablets. It explains more about the size qualifiers that you can use on values and layout folders.

New Tools For Managing Screen Sizes – a blog article by Dianne Hackborn. It explains how to use the new size qualifiers (e.g. layout-600dp, layout-sw600dp).

About Bill Lahti

Bill Lahti is a software engineer building mobile applications and knowledge management solutions. Two of his interests are writing for this blog and building Android apps, with strategy games being an area of particular interest.
This entry was posted in Android and tagged , , , , , , . Bookmark the permalink.

4 Responses to Three Variations for Image Square Grids in Android

  1. Pingback: Horizontal Scrolling Pages of Images in Android « More Is Not Always Better

  2. pinched says:

    Hey, I couldn’t find the source code. Seems like it has lost a link.
    Would be helpful if you could reupload.

    • Bill Lahti says:

      Sorry,something has changed with the Google doc viewer. I think this affects Chrome browser users. Use the download icon on the page for the demo app. The download icon is to the right of the date and the version number. That one works fine.

  3. Pingback: Android Example of a Zoomable Game Board | More Is Not Always Better

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s