Posts Tagged 'tutorial'

Drag-Drop for Android GridView (V4)

About a year and a half ago, I built an example Android app that allows you to drag images from one spot in a GridView to another. I have had several requests to show how the drag-drop framework used in that example could be adapted to the current Android support (V4+) for drag-drop operations. I have done that work and I am making the source code available in this post.

The sample app works the same as the one described in “Drag-Drop for An Android GridView“. There is a grid displayed on the screen. An “Add Image” button allows you to add images to the screen. Those images can be dragged onto the grid. Any images in the grid can be dragged around. There is also a trash can icon on the screen. Images can be dragged there to remove them from the screen. The image below shows what the app looks like on a Nexus 7 tablet.

drag-griview-v4-05

How It Works

Usually, I do an extensive write-up to explain how my demo apps work. However, this time, I am starting with a very short description in this section and posting the source code. I will add a another longer post in the coming months.

If you read my earlier article about dragging views onto a GridView, you know that the drag-drop framework I use originates with the Android Launcher code for API 8. With a combination of interface classes for DragSource, DropTarget, a DragLayer, and DragController,  the Launcher defined a useful framework for handling drag-drop operations. How I adapted those classes from the Launcher code is described in my earlier blog post. In the newer version of my GridView demo app, I have modified the classes a little bit.

DragActivity – This is the main activity of the demo app. It sets up a grid of images, defines a listener for drag events, and connects it to the views displayed on the screen.

DragSource – A drag source is a view where drag operations start. There is a DragSource interface class that defines the methods that a view must implement to be a source.

DropTarget – A drop target is a view that accepts dragged objects. An interface class describes the methods every DropTarget must implement.

DragController – This object is the controller that does most of the work to support dragging and dropping. It receives the drag events and interacts with DragSource and DropTarget objects. It also sends information back to the activity that created it. That activity is expected to implement the DragDropPresenter interface.

DragDropPresenter – This interface defines the set of methods an activity must support in order to work with a DragController.

ImageCell – An ImageCell is a subclass of ImageView. It is used to display an image on the grid. Since objects can be moved from one spot on the grid to another, an ImageCell is both a DragSource and a DropTarget.

ImageCellAdpater – The object that is used by the GridView to place ImageCell objects on the grid.

DeleteZone – This object is a DropTarget. It allows objects to be dragged onto it for disposal. It is used to implement the trash can that you see on the screen.

In this object model, here is how an object moves on the screen. The user touches a view on the screen. The view touched signals an event, onClick, to its listener, which is the DragActivity. The activity passes the event on to its DragController object. If the view is a DragSource, the controller initiates a drag operation by asking the source for its ClipData and the view to display as the drag shadow. It then has the view start sending drag events (via a call to v.startDrag). As the user moves the view around on the screen, all the events associated with drag-drop are sent to the DragController. The DragController makes everything happen from there. When the shadow view is over a drop target, the view, if it is accepting dropped objects, is informed (via method calls to onDragEnter and onDragExit) so it can provide visual feedback to the user that a potential drop spot has been found. If the user releases the object over a drop target, the target object is informed (via onDrop) so it can perform the operation. There are also method calls made back to the DragActivity so it knows when dragging begins, when it ends, and whether it was successful.

When you study the code for DragController, you will find that there are a lot of conditions being checked. That’s because of the DragController working with two kinds of views (sources, targets) and because of the way I wanted the GridView to work. You can only put one image in a cell. The cell must be empty to accept an image. If you drag an object and drop it somewhere that does not accept drops, it goes back to the cell it started in.

Source Code

You can download the source code for this demo from  the Wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at 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.2 (API 17). It works in all API levels from API 11 on up.

Potential Improvements

This sample app is an adaptation of the drag-drop framework from my earlier sample apps. The first of these (Moving Views In Android – Drag-Drop) goes back to January 2011. That one is the result of studying the Android Launcher code from API Level 8. I simplified the drag-drop framework a bit, but basically maintained its set of classes (DragSource, DropTarget, DragController, etc.).So the current example is a continuation of that framework. As described in the earlier section, you see that it has basically the same classes and interfaces.

It did not take much work to adapt to the Android drag-drop framework, which was introduced in API 11. However, it’s not clear to me that having a set of interface classes and defining View subclasses (e.g. ImageCell) is the easiest way to support drag-drop. In fact, there is a note to that effect in the “Drag and Drop” article. It says “You will probably want to use the listener in most cases. When you design UIs, you usually don’t subclass View classes …”. What I intend to try in the next month or so is another variation of this GridView example. In it, I will support dragging onto the grid, using only a listener. It will be interesting to see if that approach turns out to be easier.

Android Fragments Example

One thing I think is essential for understanding a feature in Android is a working example. The difference between a good write-up and the same write-up with source code attached is huge for me. I fully appreciate the great documentation and resources provided on the Android Developers website and on other websites. However, the working example is usually my guarantee that I will eventually understand a feature.

A case in point for me is the Fragments feature. I have been using them for about a year now so I am getting more comfortable using them in my app work. Several times in that time period, I have started apps with Fragments in Android 4+ and had to rework them so I could support older versions of Android. For me, that’s Android 2.3.3 (API 10). Fortunately, that’s easy to do because of the Android V4 Compatibility package that allows you to use Fragments in the earlier versions.

Since the need keeps coming up for me to have both Fragments and backward compatibility, I decided to build myself a simple demo app that combines both. That demo app helps me remember how to do that. Often, it is my starter kit for a new app. That’s what I am writing up and sharing in this blog post.

n7-and-lg-01

Uses for Fragments

If you have not read about Fragments, here’s my version of why they are useful. They make it easier to have UI components that you can use in different ways in your app. Two main uses: (1) handling differences between your app in landscape mode and your app in portrait; (2) handling differences in your UI when running on a tablet.

The following app screenshots illustrate the points. The first is the app on a small device in landscape. Nothing too surprising is going on. If you select a title on the left, the text for that title appears on the right. Reorient to portrait mode and you will see what’s in Figure 2. There is only room for the titles list. When you select a title, the whole screen changes. Titles are hidden and the details appear. The remaining figures show the same app on a tablet. There is more than enough screen in either orientation so the titles are always visible.

fragments-lg-02

Figure 1 – Landscape orientation, small screen

fragments-lg-01fragments-lg-01a

Figure 2 – Portrait orientation, small screen

fragments-n7-01

Figure 3 – Portrait orientation, Nexus 7 tablet, 1280 x 800

How It Works

You have to know only a few things to get this to work:

  1. How fragments can be used to display information in the two situations: standalone activity (Figure 2) and as part of a wider view (Figure 1 or Figure 3).
  2. How to structure your app to support different layouts for portrait and landscape
  3. How to structure your app to support the larger screens of tablets.
  4. How to provide backward compatibility for the Android versions prior to Android 4.

This demo example makes use of the code available with the Fragment class definition. That article explains the first two points above.

For point #3 above, I suggest reading one of my earlier blog posts, “My Dashboard User Interface on Android Tablets“. It explains how to use different layout folders to hold layout xml for different sizes of screens. Two of the most useful references in the References section “New Tools For Managing Screen Sizes”  and “Supporting Multiple Screens“.

The last point in list above has to do with using the new Fragment class in earlier versions of Android. For that, you should read about the V4 compatibility library for Android. Once I had the basic example working, I followed their instructions to make the demo example backward compatible. I did what they described in the section “Using the V4 Library APIs”.

  • Use FragmentActivity as the superclass for you own Activity classes if they are going to use Fragments.
  • When you need a FragmentManager object, call getSupportFragmentManager rather that getFragmentManager.

I did not do anything about the action bar and title bar. They are what they are in each of the Android versions. If you want to read about some options for the action bar, read my note on “Getting Started With Android V4“.

Source Code

You can download the source code for this demo from  the wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at 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.2 (API 17). It works in all API levels from API 10 on up.

Conclusion

You can find examples of fragments in many places; the same goes for V4 compatibility. So, in a way, there is not a lot new here. However, if you know you want both fragments and V4 compatibility, consider using this app as a starter for your next project.

References

Fragments

V4 compatibility library for Android

Supporting Multiple Screens

Multitouch Panning and Zooming Examples for Android

I have done some work to help me understand multitouch features in Android. I built a simple demo with examples of pinch zooming and panning. Most of the examples involve using drawing operations on the canvas of a view.

zoom-pan-blog-01zoom-pan-01

About two years ago, I started some work on moving images in Android. I started with the “Making Sense of Multitouch” blog article and ended up doing some simple things with moving images and then drag-drop based on the Android Launcher (see references for links). It’s interesting to me that I have gone back to that same article for a different purpose. I am working on apps where the basic zoom and pan operations are needed.

Overview of the Examples

1 Basic Multitouch

The first example does the basics of multitouch as explained in the Multitouch article. An image displays on the screen. You can touch the image and move it around. You can also use two fingers to do pinch-zoom and zoom out.

zoom-pan-04

I did this as the first step because I wanted to be sure that I understood the Multitouch article and had a working example.

To understand how this example works, go through the Mulitouch article and then get the source code for my examples (see below). My adaptation of the Multitouch article is in class PanZoomView.

/**
 * This view supports both zooming and panning.
 * What gets shown in the view depends on the onDraw method provided by subclasses.
 * The default onDraw method defined displays R.drawable.zoom_view_sample.
 */
public class PanZoomView extends View

The key things to understand in the article and the code are these:

  • How the canvas moves around. See the call to “canvas.translate(x, y)” in method onDraw.
  • How the canvas is scaled during a pinch-zoom gesture. See the call to :canvas.scale(mScaleFactor, mScaleFactor)” in method onDraw.
  • How onTouchEvent handles touch and multitouch events. It sets variables so the calls to translate and scale work.

2 Canvas

In the second section, there are two examples that do essentially the same thing for handling panning and zooming as the first one. The difference is that instead of an image being moved around and zoomed, it is a circle and a rectangle. Those are drawn on the canvas in the onDraw method of the view.

zoom-pan-05 zoom-pan-06

I did these as the next step in understanding how to build my views for my own app. Zooming an image was step one. Zooming a custom canvas was step 2. To help me understand pinch zoom and the point around which zooming occurs, I display a small circle at the point of focus for the scaling of the view. Place two fingers on the screen and look for that dot, halfway between your two fingers.

The Circles example supports both panning and zooming. The Rectangles example supports only zooming. I found myself getting a bit confused while trying to understand for translation on the canvas and scaling so both examples were useful to me.

3. Pan Zoom Listener

PanZoomListener is an interesting example. It comes from Java Pan / Zoom listener for Android (reference 2 below). Rather than doing a custom view every time you want to have panning and zooming, which is what all the other examples do, you simply set up a listener on a view you already have working.

FrameLayout f = new FrameLayout(context);
FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams (...);
imageView imageView = new ImageView (this);
view.addView (imageView, fp);
view.setOnTouchListener(new PanAndZoomListener(view, imageView, Anchor.TOPLEFT));

That may look like a lot to do, but it’s not nearly as complicated as what you do when you build a view of your own with custom handling of panning and zooming.

It works pretty well, but there are a few things that you’d need to adjust because not all of the operations are completely smooth. I definitely like the idea. I adapted the code and built my own example.

What you see in the example I did is an image. As soon as you touch it, it resizes and centers itself. After that, you can use pinch zoom on it. The focus of the zoom is the point between your two fingers.

zoom-pan-09 zoom-pan-10

4. Image Squares

This example was another step toward getting the right panning and zooming effects in the app that I am working on. In the app, I want to be able to draw a grid of squares on a canvas. In the example, I don’t draw the entire grid but simply some squares along a diagonal from top to bottom. This example supports zooming but not moving the canvas around as you touch the screen with one finger. The focus point of the zoom is shown with a small circle again.

zoom-pan-07

You cannot move the canvas around with a simple touch. However, if you start a pinch-zoom gesture and then move both fingers around the image does shift. I never figured out exactly what was causing that. So watch out for that if build on this example.

5. Fixed Point Zoom

Since I was having a few problems with the canvas jumping around a bit, I decided to try using a fixed point for zooming. The last  example zooms in and out around the center dot shown on the screen.

zoom-pan-11 zoom-pan-14

The code structure of this example is a bit different than the earlier ones. All of the views in these examples are subclasses of a PanZoomView class. The view used in this example is LargeGridView. It defines its own drawOnCanvas method like all the rest do, but it also defines its own onDraw method. By the time I got here, I found that the flexible framework I built in PanZoomView to explore the different aspects of panning and zooming wasn’t quite right for fixed point zooming.

Fixed point zoom is what I have ended up using in a couple of apps.

Conclusion

I have done this article a bit differently than some of my earlier blog tutorials. I have not included as much code and explanations.  I assume that most readers will dig into the code as they need to. Download the source code using the links in the next section.

What I wanted to do here is show the progression of my thinking and understanding as I worked on an interesting problem. If you are new to Android, I suggest following a similar progression. Start simple and add more complex features as your understanding increases.

Source Code

You can download the source code for this demo from  the wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at 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.2 (API 17). It works in all API levels from API 10 on up.

Source code is also available as a zip file from Google Drive.

References

Making Sense of Multitouch – I recommend this to anyone looking for a good starting point for touch handling in Android.

Java / Pan Zoom Listener for Android - Article that describes the PanZoomListener. Definitely, take a look at this one.

Moving Views in Android – Part 1 – a simple example that moves an image when you click a button

Moving Views in Android – Part 2, Drag and Drop – an example based on the Launcher code of Android 2.2.

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.

<FrameLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@color/background"
 >
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/gridView"
 android:color="@color/grid_background"
 android:layout_width="@dimen/image_grid_width"
 android:layout_height="match_parent"
 android:columnWidth="@dimen/image_thumbnail_size"
 android:horizontalSpacing="@dimen/image_thumbnail_spacing"
 android:numColumns="@integer/grid_num_columns"
 android:stretchMode="none"
 android:verticalSpacing="@dimen/image_thumbnail_spacing" >
 </GridView>
</FrameLayout>

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.

<resources>
 <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>
</resources>

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.
 mGridView.getViewTreeObserver().addOnGlobalLayoutListener(
 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;
         mAdapter.setNumColumns(numColumns);
         mAdapter.setItemHeight(columnWidth);
      }
   }
 }
 });

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) {
     return;
   }
   mItemHeight = height;
   mImageViewLayoutParams = new GridView.LayoutParams(mItemHeight, mItemHeight);
   notifyDataSetChanged();
}

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.

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

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.

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/frame"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" 
 android:padding="0dp"
 android:background="@color/debug_background"
 >
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/gridView"
 android:color="@color/grid_background"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center" 
 android:gravity="center_horizontal|center_vertical"
 android:columnWidth="@dimen/initial_image_thumbnail_size"
 android:numColumns="@integer/grid_num_columns"
 android:padding="0dp"
 android:stretchMode="none"
 android:horizontalSpacing="@dimen/image_thumbnail_spacing"
 android:verticalSpacing="@dimen/image_thumbnail_spacing" >
</GridView>
</LinearLayout>

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.

mGridView.getViewTreeObserver().addOnGlobalLayoutListener(
 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);
       mAdapter.setNumColumns(mNumColumns);
       mAdapter.setItemHeight(mImageThumbSize);
     }
   }
 });

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 wglxy.com website. Click here: download sources from wglxy.com. 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.

References

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).

Android Images With Clickable Areas – Part 1

I want to display images in Android and have different clickable regions within the image. I have read about two different ways of doing this.  The first is a bit like image maps in HTML. You define areas on the screen, using a list of coordinates, and connect the areas to actions. The second method  involves overlaying a hotspot image on top of the real image being displayed. The overlay image has exactly the same size as the first image. It uses different colored regions  to indicate the hotspots. When the user clicks on the primary image, code runs to check the pixel at the corresponding point in the hotspot image. In this, the first of two articles about image maps in Android, I will explain what I learned for the second technique.

Let’s start with what the app looks like when you touch the screen. Circles appear that indicate roughly where the clickable regions on the screen are. When you touch one of the regions, the image changes.

For example, when you touch the image near the end of the space ship , the app changes to make it look like you started the space ship.

Touching the image a second time, returns you to the initial view of the app, which is the first picture without the orange circles showing.

The way this app is set up with a FrameView and ImageView gives you an easy way to handle scaling. I used a 800 x 480 image for this demo app. It scales automatically to all screen sizes, and it is high enough resolution that it looks good on a tablet. Here’s what it looks like in a portrait orientation.

In a real app, you might disallow portrait orientation because it has a very small usable view. I left it in for testing reasons. I wanted to know that the image and the hotspot image were scaling correctly.

How It Works

Most of what goes on in this app is straightforward stuff in Android: clickable images, changing the drawable of an ImageView, etc. The really interesting part for me was getting the scaling done automatically. Two parts of that were tricky: (1) getting the layout just right so the images would scale correctly on all Android devices and tablets; (2) getting the clickable regions to work and also to scale automatically.

Two images are used in the main layout for this app. They are shown below. The first is the view of the starbase that you have seen above. The second image has several rectangles on it. If you were to overlay the second image on top of the first, you would see that they are close to the stops were the circles were drawn to indicate clickable regions.

The overlay image has exactly the same size as the first image. It uses different colored regions  to indicate the hotspots. When the app is running, the overlay image is used to locate a hotspot, which is a region on the screen that you want to define an onClick handler for. When the user clicks on the primary image, code runs to check the pixel at the corresponding point in the hotspot image. I will explain that a bit later. First let’s look at how the images are laid out on the screen.

The main layout for this app uses a FrameView. It is defined to fill up the entire screen. Within it are two image views. Views defined this way in a FrameView share the space of the frame. The second one appears on top of the first one.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/my_frame"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="@color/background" >
<ImageView 
 android:id="@+id/image_areas"
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" 
 android:scaleType="fitCenter"
 android:visibility="invisible"
 android:src="@drawable/p2_ship_mask" 
 />
 <ImageView
 android:id="@+id/image"
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" 
 android:scaleType="fitCenter"
 android:src="@drawable/p2_ship_default"
 />

</FrameLayout>

Notice that the image view that shows the masks image is invisible. When the app runs, you do not see it, but the layout manager has included it in the layout. Since both ImageViews use “fill_parent” (same as “match_parent”), they expand to fill the parent view. Scaling is done according to the rules for “fitCenter”, which means centering the image both horizontally and vertically and scaling until either the width or the height matches the parent view’s dimension.

With this layout in place, all that is needed is code in the Activity to handle the screen being touched. The main activity implements View.onTouchListener. The method required by that interface is the onTouch method. Here is that method, shortened a bit to make it easier to explain.

public boolean onTouch (View v, MotionEvent ev) {
 final int action = ev.getAction();
 // (1) 
 final int evX = (int) ev.getX();
 final int evY = (int) ev.getY();
 switch (action) {
 case MotionEvent.ACTION_DOWN :
   if (currentResource == R.drawable.p2_ship_default) {
   nextImage = R.drawable.p2_ship_pressed;
   } 
   break;
 case MotionEvent.ACTION_UP :
   // On the UP, we do the click action.
   // The hidden image (image_areas) has three different hotspots on it.
   // The colors are red, blue, and yellow.
   // Use image_areas to determine which region the user touched.
   // (2)
   int touchColor = getHotspotColor (R.id.image_areas, evX, evY);
   // Compare the touchColor to the expected values. 
   // Switch to a different image, depending on what color was touched.
   // Note that we use a Color Tool object to test whether the 
   // observed color is close enough to the real color to
   // count as a match. We do this because colors on the screen do 
   // not match the map exactly because of scaling and
   // varying pixel density.
   ColorTool ct = new ColorTool ();
   int tolerance = 25;
   nextImage = R.drawable.p2_ship_default;
   // (3)
   if (ct.closeMatch (Color.RED, touchColor, tolerance)) {
      // Do the action associated with the RED region
      nextImage = R.drawable.p2_ship_alien;
   } else {
     //...
   }
   break;
  } // end switch
  if (nextImage > 0) {
    imageView.setImageResource (nextImage);
    imageView.setTag (nextImage);
  }
  return true;
}

There is a lot going on in this method:

(1) The coordinates of the touch point come from the event object. The coordinates are relative to the view.

(2) Given the coordinates of the touch, we look up the color of a pixel at the corresponding point in the mask image (the one with the colored rectangles). The layout rules for a FrameView and identically sized images ensure that the points correspond. The code for that follows:

public int getHotspotColor (int hotspotId, int x, int y) {
  ImageView img = (ImageView) findViewById (hotspotId);
  img.setDrawingCacheEnabled(true); 
  Bitmap hotspots = Bitmap.createBitmap(img.getDrawingCache()); 
  img.setDrawingCacheEnabled(false);
  return hotspots.getPixel(x, y);
}

(3) The color for point is not necessarily the exact color value we used in the hotspot image. The reason for this is that the colors could change a bit as the image is scaled. We use a new ColorTool object to test for a matching color.

public boolean closeMatch (int color1, int color2, int tolerance) {
 if ((int) Math.abs (Color.red (color1) - Color.red (color2)) > tolerance ) 
    return false;
 if ((int) Math.abs (Color.green (color1) - Color.green (color2)) > tolerance ) 
    return false;
 if ((int) Math.abs (Color.blue (color1) - Color.blue (color2)) > tolerance ) 
    return false;
 return true;
} // end match

That’s all the code parts. Let’s summarize what happens as the app runs.

  • The user touches the screen.
  • Because that Activity has an onTouch handler, the method onTouch gets called.
  • The code there looks at the event argument to get the x-y position of the touch.
  • These are coordinates relative to the origin of the ImageView, which is itself embedded in the FrameView.
  • Given the x-y location of the touch, the code locates the hidden hotspot image and finds the pixel at the corresponding location.
  • It then takes the color there and finds the best match for it in the following colors: WHITE, RED, BLUE, YELLOW.
  • It then takes the action defined for that color.

For this demo, I used a simple image editing program: Mac Paintbrush. I took an image and made a copy so I could see where I wanted to add the clickable regions.  I added the three rectangles for the colors first. Then I added white over everything else. I saved that as PNG file.

Source Code

You can download the source code form the wglxy.com website. Click here: download sources from wglxy.com. 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 2.3.3 (API 10). It works in all API levels after API 8.

Conclusion

This way of adding clickable regions is easy to do and takes full advantage of Android’s handling of different screen sizes, screen densities, and orientations. You do not incur much overhead for the second overlay image. In my case, it was only 4 KB. The original image, as a png, was about 70 KB. For simple transitions from one activity to another, this method works out pretty well.

In my next note, I will take a look at another method of doing image maps and redo this example.

References

  • Overlay to make parts of image clickable – discussion in the Android Developers group. This is where I learned about the overlay image method.
  • ImageMaps for Android – This is a very good example of doing images maps by defining regions in xml files. It displays a US map where you can touch the different states. Something like this will be the subject of my next article on image maps.
  • freepik spaceship – The spaceship image came from the freepik website. It is free for noncommercial use.

Horizontal Scrolling Pages of Images in Android

With the Android V4 compatibility library, you can use Fragments even if you are not using Ice Cream Sandwich (API 14+). That opens up some interesting possibilities for your applications, particularly if you want to be more creative in your use of the extra space available on tablets. For this article, I want to focus on another use for fragments:  horizontal scrolling pages.

I started with an article on the Android Developers’ blog: Horizontal View Swiping With View Pager. It is a very useful article that shows you how to get pages of lists scrolling. I wanted to build a demo app of my own that uses pages. Having spent some time reading the new Android Design website, which places heavy emphasis on visual elements, I decided to figure out how to have pages of images that scroll left and right. My inspiration was the section named “Pictures Are Faster Than Words” on the Design Principles page.

My demo app shows pages of images. It has a structure very similar to the Fragments demo that is in the support package. I started with that and replaced the ListView fragments with GridView fragments. Each GridView holds images. Each of the images has a title. When you long-click on an image, you zoom in on the picture, and from there you can click the picture to see the text for the topic. The figures below show what the app looks like.

Pages of topics scroll left and right, or you can jump to the start or the end of the images by touching the “First” and “Last” buttons. To zoom in on the topic of an image, you use a long-click (long press). I did that because the regular touch events are being used so you can scroll pages. A second touch on the enlarged image takes you to the text associated with the image.

In landscape mode, a different arrangement of images exists. On a small device, it shows one row with three images rather than the 2×2 arrangement in portrait mode.

Figure 5 – Layout of images varies in portrait and landscape

Source Code

The source code for this demo application is available in two places:  (1) download from Wglxy.com. (2) download from Google Docs.  Do a “clean build” in Eclipse if it reports errors immediately after importing the project.

This application has been compiled with API level 10 (2.3.3). It runs on devices and in the emulator from API level 8 to API level 15. It also compiles with 4.03. If you prefer to work at that level, just change the project properties. If you do start your app at the higher level, be sure to do extensive testing at the lower API levels. You want to make sure that you are not using features introduced in API 15 that are not supported in older versions.

How It Works

This demo depends on the ViewPager class in the V4 Compatibility Library. If you download the source code for my demo, you do not have to worry about installation of the support package. It is already in the app libs folder and the project’s build path has already been set correctly. (When you set up your own project, you will want to do those two steps for your project. See the instructions for compatibility library and read Horizontal View Swiping With View Pager. )

The main activity of the demo is the GridViewPager class. It depends on the ViewPager class, which works a lot like ListView and GridView classes. You implement an adapter class that provides the views that it scrolls. The adapter is called by a ViewPager view that you include in the layout for the activity. GridViewPager uses demo_pager.xml for its layout. Inside a FrameLayout and LinearLayout, there is a definition of a ViewPager.

<android.support.v4.view.ViewPager
 android:id="@+id/pager"
 android:layout_width="match_parent"
 android:layout_height="0px"
 android:layout_weight="1">
 </android.support.v4.view.ViewPager>

This definition in the layout file indicates where we want the scrolling pages to appear. For this demo, it is right above the definitions for the First and Last buttons.

Let’s examine the GridViewPager class in greater detail. Because it uses fragments, it is a subclass of FragmentActivity, which is one of the classes provided by the compatibility library.
public class GridViewPager extends FragmentActivity 
implements View.OnLongClickListener

In the onCreate method of GridViewPager, an adapter object is created. The adapter there is a bit like an adapter you create when you use ListView and GridView in Android. Rather than providing views that can appear in a list or in a grid, this adapter provides a set of views that can be scrolled left and right as pages. It uses a layout that embeds the ViewPager view from the compatibility class. As shown below, onCreate starts out by defining its content view using the demo_page.xml file. Then there are a few lines to define a collection of images and topics to be used in the demo. Then there is the creation of an adapter object that does the work of creating each individual page. Note the arguments on the constructor. MyAdapter has access to the entire list of topics and the Resources object so it can divide the images up among the pages.

protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.demo_pager);
 // Create a TopicList for this demo. Save it as the shared instance in TopicList
 Resources res = getResources ();
 String sampleText = res.getString (R.string.sample_topic_text);
 TopicList tlist = new TopicList (sampleText);
 TopicList.setInstance (tlist);
 // Create an adapter object that creates the fragments that we need 
 // to display the images and titles of all the topics.
 mAdapter = new MyAdapter (getSupportFragmentManager(), tlist, res);
 mPager = (ViewPager)findViewById(R.id.pager);
 mPager.setAdapter(mAdapter);

 ...
}

Class MyAdapter is defined as a subclass of FragmentStatePagerAdapter, which in turn is a subclass of FragmentPagerAdapter. That class is designed to work with the ViewPager layout class. Its main responsibility is to provide the page views.

public static class MyAdapter extends FragmentStatePagerAdapter
  private TopicList mTopicList;
  private int mNumItems = 0;
  private int mNumFragments = 0;
public MyAdapter (FragmentManager fm, TopicList db, Resources res) {
  super(fm);
  setup (db, res);
}

Note that there are instance variables for the number of images (items) and the number of page fragments. Those are calculated inside the setup method. They are used inside the getCount and getView methods.

/**
 * Get the number of fragments to be displayed in the ViewPager.
 */
@Override public int getCount() {
 return mNumFragments;
 }
/**
 * Return a new GridFragment that is used to display n items at the position given.
 *
 * @param position int - the position of the fragement; 0..numFragments-1
 */
@Override public Fragment getItem(int position) {
 // Create a new Fragment and supply the fragment number, image position, 
 // and image count as arguments.
 Bundle args = new Bundle();
 args.putInt("num", position+1);
 args.putInt("firstImage", position * mNumItems);
 // Most pages hold mNumItems. The last page might not have the full number of items.
 int imageCount = mNumItems;
 if (position == (mNumFragments-1)) {
    int numTopics = mTopicList.getNumTopics ();
    int rem = numTopics % mNumItems;
    if (rem > 0) imageCount = rem;
 }
 args.putInt("imageCount", imageCount);
 args.putSerializable ("topicList", TopicList.getInstance ());
 // Return a new GridFragment object.
 GridFragment f = new GridFragment ();
 f.setArguments(args);
 return f;
}

You should be able to see the similarity between this kind of adapter and the ones you use for list views and grid views. For this adapter, getCount returns the number of pages (fragments) rather than the number of items in the list. The getItem method returns a fragment object to be displayed as a page.

Let’s look at GridFragment, which is a subclass of Fragment. It is the object that creates a page of images. Each page uses a GridView object. The number of rows and columns is determined by two integer resource values: grid_num_rows and grid_num_cols. The code section below highlights some of the key parts of GridFragment.

public class GridFragment extends Fragment {
... 
@Override public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 // Read the arguments and check resource values for number of rows and number of
 // columns so we know how many images to display on this fragment.
 Bundle args = getArguments ();
 mNum = ((args != null) ? args.getInt ("num") : 0);
 ...
}
public void onActivityCreated(Bundle savedInstanceState) {
 super.onActivityCreated(savedInstanceState); 
 // When the activity is created, divide the usable space in the view into columns
 // and put a grid of images in that area.
 Activity a = getActivity ();
 ...
 // Connect the gridview with an adapter that fills up the space.
 gridview.setAdapter (new GridImageAdapter (a, mTopicList, 
                                            mFirstImage, mImageCount, 
                                            cellWidth, cellHeight));
 ...
}
public View onCreateView (LayoutInflater inflater, ViewGroup container, 
                          Bundle savedInstanceState) {
 // Build the view that shows the grid.
 View view = inflater.inflate(R.layout.demo_pager_grid, container, false);
 ...
 return view;
}
} // end class GridFragment

There are few things worth noting in the code above:

  • onCreateView is an important method in any Fragment.
    It gives you the opportunity to define the user interface for the fragment. Here, it inflates the layout defined in demo_pager_grid.xml. That’s where the GridView definition is.
  • A GridImageAdapter is a BaseAdpapter subclass that does a bit more work than other adapter classes you might have built.
    It takes a few more arguments in its constructor. The reason I did this is so I could more precisely control  how many images are displayed in the GridView. For a 2×2 grid, for example, I wanted exactly four images to be displayed, and I wanted all of them to be displayed without having to scroll vertically inside the GridView.
  • GridImageAdapter defines a getItem method that creates the views that show up in the GridView.
    Each of the those views is defined by demo_pager_grid_item.xml. It contains a FrameLayout with an ImageView and TextView inside it.
  • onCreateView is an important method in any Fragment.
    It gives you the opportunity to define the user interface for the fragment. Here, it inflates the layout defined in demo_pager_grid.xml. That’s where the GridView definition is.

Grid Layouts

Upon orientation change, the main view of the application switches from a 2×2 grid to a 3×1 grid. That is controlled by having different resource files in the values folders of the application. The number of rows and columns in the grid come from resource values in the dimens.xml file. Check those two files in the values and values-land folders.

Integers in values/dimens.xml:

<integer name="grid_num_rows">2</integer>
<integer name="grid_num_cols">2</integer>

Integers in values-land/dimens.xml

<integer name="grid_num_rows">1</integer>
<integer name="grid_num_cols">3</integer>

Note that there are additional values folders: values-large, values-large-land, values-sw600dp, values-sw600dp-land. Each of those has a dimens.xml file. Those files are used for large screens and tablets. If you follow this approach, you can build an app where the number of images per page is larger for tablets.

I first used this technique when I was adapting my dashboard user interface to tablets.

Rough Spots

Complexity

I know this demo is bit more complicated than some of the ones I have put together. It has pages of views that are created by an adapter object. Each of the pages has a GridView, and those are also created by an adapter object, but it’s a different kind of adapter object. If you find yourself getting confused by all the different classes, go back to the original example in “Horizontal View Swiping With View Pager“. Be sure you understand paging and fragments well. Then come back to this example.

Managing Bitmaps

A problem I did not address is managing bitmaps. I used relatively small images and not very many of them. I also used the FragmentStatePagerAdapter rather than the other FragmentPagerAdapter. It does something so it holds only the views it needs in memory.

When I get ready to move beyond a demo to a real application, I plan to study “Displaying Bitmaps Efficiently“.

GridView of Images

I wanted to create an app where you could scroll through all the images with horizontal scrolling, rather than the vertical scrolling you can get easily with a GridView. Something that probably needs more work is the way in which I create GridView objects. I came up with a way to use a GridView for each page of images, but I had to go to some extra effort to size the images so there would be no need for vertical scrolling inside the pages.

I am not sure I did that in the easiest manner. I used a DisplayMetrics object and dimension values in dimens.xml.

Activity a = getActivity ();
Resources res = a.getResources ();
DisplayMetrics metrics = new DisplayMetrics();
a.getWindowManager().getDefaultDisplay().getMetrics(metrics);
// From the resource files, determine how many rows 
// and columns are to be displayed.
final int numRows = res.getInteger (R.integer.grid_num_rows);
final int numCols = res.getInteger (R.integer.grid_num_cols);
...
int availableHeight = metrics.heightPixels - heightUsed; 
int availableWidth = metrics.widthPixels - widthUsed;
int cellWidth = availableWidth / numCols;
int cellHeight = availableHeight / numRows;

By starting with metrics.heightPixels and widthPixels, I had to account for all the different elements that took up vertical and horizontal space.

Since starting this example app, I have found a few examples where people have used TableLayout objects for a collection of images. I intend to take a look at that class and see if that might be a better way to lay out the topic images.

(Update: August 31, 2012. I found a good way to set image sizes for a good fit within a grid while doing another demo app. See my post on Image Squares.)

Converting to Android Ice Cream Sandwich

There is a quick way to change this demo so it works on Ice Cream Sandwich. Follow my guide for getting started with ICS. Change the app so it is being compiled by Android 4.03 (or later). Edit the styles.xml file in the values-v14 in the resource folder. The styles file contains the following:

<resources>
  <!-- <style name="Theme.Demo" parent="@android:style/Theme.Holo.Light">
  </style> -->
  <dimen name="title_bar_height">36dp</dimen>
</resources>

Remove the comment markers around the style element. (The comment markers are there so the application builds correctly with Android 2.3.3.)

Basically, for the initial transition to V4, all that you are doing is specifying which of the two Ice Cream Sandwich (ICS) themes you want to use for the title bar. The two choices are Theme.DeviceDefault and Theme.Holo.Light. Running ICS in the emulator, the app looks like this:

Figure 6 – Demo app running on Android ICS

References

Fragments – detailed information about Fragments and how they fit in with Activity objects.

Horizontal View Swiping With View Pager – a great article that explains how to use Fragments for pages that you can scroll horizontally. This was the starting point for my demo app. I modified the example to have pages of images in grid views.

Compatibility Library- information about the Android support package that allows you to use some V4 (Ice Cream Sandwich) features in Android 2.x applications.

Supporting Tablets and Handsets – provides guidance on supporting both worlds: single and multi-pane applications.

Layout Tricks: Merging Layouts – contains an example of how to stack a TextView on top of an ImageView.

New Tools For Managing Screen Sizes – a blog article that explains how to use the new size qualifiers (e.g. values-sw600dp, layout-sw600dp).

Getting Started With Android Ice Cream Sandwich (ICS)

How and when do you make the transition to Android Ice Cream Sandwich (API 14+)? That’s a question I have been asking myself for existing apps and new ones. The relative market share of ICS is still very small — under 4%, as shown in the current report on distribution of devices by Android platforms. Still, I want to be looking ahead so there is less work to do at the time when ICS becomes the Android platform with the largest share.

What I intend to do for existing apps is stick with the themes and styles I had been using for pre-ICS (2.x) devices. For ICS devices, I want to move right away to the new themes provided with Ice Cream Sandwich. The simplest example of how to do this is the HelloWorld app that you get when you create a new Android project in Eclipse. By adding the right style definitions for theme in the layout folders, you can easily get an app with a title bar that is correct for the target platform.  Figure 1 shows the Hello app on a pre-ICS device. Figure 2 shows the app on an ICS device.

  

Figures 1 – 2

I consider this a reasonable first step toward Ice Cream Sandwich. A few other steps are suggested in the last section below.

First Step – Defining the Theme

In order to set up the Hello World app to look right on ICS and pre-ICS devices, all you have to do is set up  theme definitions in two styles.xml files. One goes in the regular res/values folder. The other goes in the special res/values-v14 folder, which indicates that is used only when the API level is 14 or greater.

values
        styles.xml
values-v14
        styles.xml

Inside each of styles.xml files is a definition for the resource named “MyTheme”. When the API level is 14 or higher, the style definition that is used is the one in values-14 styles.xml file. Here is what the two definitions look like:

File values/styles.xml contains:

<resources>
 <style name="MyTheme" parent="@android:style/Theme">
 <!-- Any customizations for your app running on pre-3.0 devices here -->
 </style>
</resources>

File values-v14/styles.xml contains:

<resources>
 <style name="MyTheme" parent="@android:style/Theme.DeviceDefault"></style>
</resources>

The MyTheme style is used in the AndroidManifest to indicate what the default style is for activities in the application.  The key line is the “android:theme” line inside the application definition.

<application
 android:theme="@style/MyTheme"
 android:icon="@drawable/ic_launcher"
 android:label="@string/app_name" >
 <activity
 android:name=".HelloWorldActivity"
 android:label="@string/app_name" >
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>

Note that there are two themes to try in ICS. There is “Theme.Holo.Light” and “Theme.DeviceDefault”. The Holo.Light theme gets you a title bar that has a light background.

All of this is very well explained here: Holo Everywhere (in the section “Using Holo While Supporting 2.x”). The author says, ”Most Android developers will still want to support 2.x devices for a while as updates and new devices continue to roll out.”. That’s an important point.

Source Code

The source code for this demo application is available in two places: (1) download from Google Docs; (2) download from Wglxy.com. The Google Docs zip file is a shared file, which means it should be available for everyone. Still, if  you find that you cannot download it from Google Doc, try the second location.

This application has been compiled with API level 15 (4.0.3). It runs on devices and in the emulator from API level 8 to API level 15. If you start your app this way, be sure to do extensive testing at the lower API levels. You want to make sure that you are not using features introduced in API 15 that are not supported in older versions.

Next Steps

Following this note gets you started with Ice Cream Sandwich (ICS). You will have a Hello World app that you can use as  a starter for other apps. Each of the apps will look like it is an app designed for ICS, at least as far as the title bar is concerned. And for the earlier API levels, it will look just the way you want it there too.

Once you have taken the first step, what should you do next? I don’t have a lot of suggestions to make at this time. What I intend to do is study more of the new features in ICS, including fragments, action bars, etc. I have a found a few resources that might help:

  • Action Bar Compatibility - a compatibility library you can use if you want to build an app that looks the same, no matter whether it is API 8 or API15. The library supports a subset of ICS features.
  • ActionBarSherlock
    If you want the new ICS look and many of the new features of ICS, you should take a look at this package. It makes it so your app looks like an ICS app, even if it is running on an API level before 4.0.

I have to admit that this is a very simple demo app. What is interesting for me is how many times I have had to redo this first step. Between my work projects and my personal projects, I have done this about five times already. Each time I have to start searching the web and the good starting point (the Holo Everywhere note) never seems to make an impression. Whenever I find something where I cannot remember the solution, even a simple one, I know it’s time to write it down and put it in a place where I can find it easily with a Google search.

My Android Programming Tutorials By Topic

I have been writing about Android for about a year and a half now. There are over twenty tutorials and articles on a variety of topics. I refer back to my own posts often, to help me on whatever my current project happens to be. Lately, I am having trouble finding things here, and I feel the need for a little organization. Search for this blog works well, as does the Tags section in the right-hand column. However, sometimes I prefer to find things using a hierarchy of topics. So that’s what this note is.

Notes, grouped by category:

Drag and Drop
User Interface / Dashboard
Android Background Tasks
Splash Screens
Activities
Drawing
Android/Java Performance Tuning
Android Environment
My Android Game App

Most of the articles listed below are tutorials that include the full Android source code.

Drag and Drop

Moving Views in Android – Part 1
I wrote this as I started trying to understand how to use the touch interface to move objects around on the screen. It turns out that what I did was more about getting images to move than getting views to move.

Moving Views in Android – Part 2: Drag and Drop
I studied the Android Launcher code to see how they supported dragging and dropping views. I then built an example based on that code. Views really move.

Moving Views in Android – Part 3: Drop Zones
I extended Part 2 a bit to illustrate the Launcher’s DropTarget interface. I called them “Drop Zones”. They represent areas on the screen where dragged views can be dropped. The example also shows how to add movable views dynamically.

Drag-Drop for an Android GridView
I built a demo that allows you to add images dynamically to the screen and then drag them onto a GridView. You can then move the images around in the GridView or move them to the trash.

Drag-Drop for Android GridView (V4)   - An updated version of the GridView example. This one shows drag-drop for a GridView, using the latest drag-drop handling in Android V4.

User Interface / Dashboard

How To Build A Dashboard User Interface
This note describes a small demo app that illustrates the Dashboard user interface pattern. The main screen has a title bar and six buttons to take you to the different sections of the app.

Examples of the Dashboard Pattern – links to examples of Android dashboard user interfaces.

Presentation on the Dashboard User Interface – a presentation I did for Tridroid, a meetup group in the Triangle area of North Carolina.

Adapting My Dashboard UI To Tablets The demo  builds on “How To Build A Dashboard User Interface”. The app shows one way to adapt an Android app for tablets. (Note: This article is obsolete. See  next paragraph.)

My Dashboard User Interface on Android Tablets
This tutorial shows how to have a dashboard user interface that works on both phones and tablets. It extends the user interface presented in “How To Build A Dashboard User Interface”.

Android Fragments Example - a simple example for Fragments that is also backward compatible to Android API level 10.

Horizontal Scrolling Pages of Images 
This one makes use of fragments to break up a gallery of images into separate pages. Pages of images scroll left and right, or you can jump to the start or the end of the images by touching the “First” and “Last” buttons.

Android Images With Clickable Areas  - This tutorial explains how to have multiple areas within an image that you can touch/click. These hotspots within the image are a bit like image maps in Html.

Three Variations for Image Square Grids - This note describes different ways to have square images on a square grid.

Android Background Tasks

AsyncTask Example appLong Computations in the Main Android Thread
Five steps for moving expensive computations out of the user interface thread.

Using Android AsyncTask for Background Tasks
My previous note explained one way to take an expensive operation and move it to a background task. Since writing that, I have learned about the AsyncTask class. It is much easier way to do the same thing.

One Way to Cancel an AsyncTask - a note about stopping a task that was started for a long-running background task.

Splash Screens

Good Articles About Android Splash Screens – references I used to learn about splash screens.

Splash Screen Demo App – a simple demo app for a splash screen

Splash Screen Demo With Sound – a splash screen with a bit of sound.

Activities

My Introduction to Android Activities – a tutorial about Android activities and the activity lifecycle.

Drawing

How to save jpeg files – a demo app from back in November, 2010. I was learning how to turn a view into a jpeg file and save it on the device.

Android/Java Performance Tuning

My Guide to Performance Tuning for Android and Java – performance tuning suggestions, covering both analysis of cpu time and object allocation.

Notes on Performance Tuning of Java – describes the simple profiling tool built in to the Java runtime environment: HPROF.

Android Environment

Viewing Android Source Code in Eclipse – explains how to view Android platform source while working in Eclipse.

How to Export and Import Android Virtual Devices – explains how to move Android virtual device (AVD)  files from an Android Emulator on one machine to another machine.

What to do when your Android Emulator will not start from Eclipse

My Android Game App

Gomoku League available in the Android Market – the announcement about the release of my first game app.

Gomoku League Presentation at Tridroid – a presentation to the Tridroid Android users’ group. It describes the game of Gomoku, the idea for a league app, how I built the app, and some of the things I learned about Android and the Android market.

Amazon App Store announcement for Gomoku League - the Gomoku League app is available in the Amazon App Store.



Follow

Get every new post delivered to your Inbox.

Join 72 other followers