ImageMap for Android

Update September 12 2013

I have disable comments for this post. I am amazed that this old code is still getting so much attention. I feel that the code has become dated and needs a good refactoring. It needs to be tested with newer version of android SDKs. The touch handling needs to be cleaned up. If you find yourself still compelled to use this code, and you have questions/issues, PLEASE direct them to the GITHUB issues list for the project.

GitHub.

Update March 6 2013

Thank you for all of the interest in this widget.

I have become a bit overloaded with work in recent months and I have regrettably been unable to reply to comments in anything near a timely manner.

If you have a question about features or have run into a shortcoming or bug in this widget, I am recommending that you open an issue on GitHub.

This will provide me a better way of tracking problems and questions and may enable you to get help from other people who have used/forked the repo.

As my time frees up (hopefully) I will do my best to create examples and fix bugs. This widget is really due for a hard core refactoring.

  • original post -

I found myself needing to display an image and have the user tap on different parts of the image to navigate and perform actions.  After sketching out what I needed, it became apparent that what I wanted was something like an HTML map tag, but in an Android view.  A bit more thinking and I decided this would be a useful widget to have available.

The resulting code:

  • Supports images as drawable or bitmap in layout

  • Allows for a list of area tags in xml

  • Enables use of cut and paste HTML area tags to a resource xml  (ie, the ability to take an HTML map and image and use it with minimal editing)

  • Supports panning if the image is larger than the device screen

  • Supports pinch-zoom

  • Supports callbacks when an area is tapped.

  • Supports showing annotations as bubble text and provide callback if the bubble is tapped

The code can be found in GitHub - Android Image Map

Update 2 March 2012: Added an option to always resize the image using the original image bits. Certain images, when resized up and down lose a lot of their image quality. Always resizing from the original bits gives the best possible image, but requires extra memory. The tradeoff is now up to you.

Here are some quick notes about the implementation, I'll make some follow up posts with more details about some of the key areas.

The ease of associating the map with the img matches the ease of doing so in an img tag in HTML.  Just supply the name of the map to use in the layout:

<!--?xml version="1.0" encoding="utf-8"?-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ctc="http://schemas.android.com/apk/res/com.ctc.android.widget"
    android:orientation="vertical"
    android:layout\_width="fill\_parent"
    android:layout\_height="fill\_parent"
>
<com.ctc.android.widget.ImageMap
    android:id="@+id/map"
    android:layout\_width="fill\_parent"
    android:layout\_height="fill\_parent"
    android:src="@drawable/usamap"
    ctc:map="usamap"/>
</LinearLayout>

The area map is specified in your project at res/xml/map.xml

One difference over HTML maps is that each area must have an id. I went back and forth on this requirement, and I may change the code to allow for areas without id. The code will use the name attribute if present, otherwise it will look for title or alt. The current implementation also requires a name for each area, though this could be changed easily.

<!--?xml version="1.0" encoding="utf-8"?-->
<maps xmlns:android="http://schemas.android.com/apk/res/android">
<map name="gridmap">
<area id="@+id/area1001" shape="rect" coords="118,124,219,226" />
<area id="@+id/area1002" shape="rect" coords="474,374,574,476" />
<area id="@+id/area1004" shape="rect" coords="710,878,808,980" />
<area id="@+id/area1005" shape="circle" coords="574,214,74" />
<area id="@+id/area1006" shape="poly" coords="250,780,250,951,405,951" />
<area id="@+id/area1007" shape="poly" coords="592,502,592,730,808,730,808,502,709,502,709,603,690,603,690,502" />
</map>
<map name="usamap">
<area id="@+id/area1" shape="poly" coords="230,35,294,38,299,57,299,79,228,79" />
...
</map>
</maps>

The image itself is placed in res/drawable-nodpi so that the system will not attempt to fit the image to the device based on dpi. This way we are guaranteed that our area coordinates will map properly to the displayed image.

Here is a sample activity that finds the view in the layout and adds an on click handler

public class ImageMapTestActivity extends Activity {
    ImageMap mImageMap;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // find the image map in the view
        mImageMap = (ImageMap)findViewById(R.id.map);
        // add a click handler to react when areas are tapped
        mImageMap.addOnImageMapClickedHandler(new ImageMap.OnImageMapClickedHandler() {
            @Override
            public void onImageMapClicked(int id) {
                // when the area is tapped, show the name in a
                // text bubble
                mImageMap.showBubble(id);
            }
            @Override
            public void onBubbleClicked(int id) {
                // react to info bubble for area being tapped
            }
        });
    }
}

There were lots of challenges with this view. I'll post some follow up thoughts over the next few days along with a bit of discussion about the implementation.

Enjoy the code!