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 spots where 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.

About Bill Lahti

Bill Lahti is an indie game developer. Latest project: an exploration game. Recent game: Hop, Skip, and Thump, a simple abstract strategy game. Other iOS and Android games: Double Star II, Wing Leader, Gomoku League.
This entry was posted in Android and tagged , , , , . Bookmark the permalink.

96 Responses to Android Images With Clickable Areas – Part 1

  1. Pingback: My Android Programming Tutorials By Topic « More Is Not Always Better

  2. Chitranshu says:

    thoroughly analysed.. and thoroughly explained..
    I got try this for sure.. 🙂
    thanks..

  3. Lasse says:

    A great and simple implementation. A really nice explanation thank you! When will you be posting the part two? 🙂
    Keep up the great work!

  4. Great explanation, so easy to understand, thanks for the valuable information…

  5. Sisko says:

    Superb tutorial and it works perfect with activity. Would it be possible to make it work in fragment?

    • blahti says:

      Yes. This could work in a fragment. What matters is the layout and that layout could be used with a fragment easily. What I have tended to do with fragments is define an interface for the fragment so that the clicks in the views in the fragment become callbacks to the activity that holds the fragment. I do not have any simple examples around. The only thing I have written about fragments is my Horizontal Scrolling article. You could check that and its references and see if that helps you.

  6. david says:

    Many thanks for your blog. Is wonderfoul.
    When will you have the first way published?. If you don’t know, do you know some url or tutorial to can see it.
    Thanks and sorry for my english!

  7. Pingback: Android: Placing button relative to ImageView : Android Community - For Application Development

  8. steve says:

    Any ideas why It doesn’t work when I download and import the project? I’m not new to Java but am new to android so could be me….

    • blahti says:

      A couple of things:
      (1) Check the Project Properties and see which Android it depends on. The original code used 2.3.3. If you do not have that installed, select the one of the versions of Android you have installed.
      (2) On the Project menu, there is a “clean…” item. Run that. Sometimes Eclipse fails to do that automatically for imported code.

      Those are two suggestions, but I should also ask what does “it doesn’t work” mean? Does it mean it does not compile or that it will not run in the emulator?

  9. terence ng says:

    the colors could change a bit as the image is scaled? Is this situation happens in all image format?

    • blahti says:

      You mean format as in png and jpg, right? That sounds right. I have not done a lot with images so I don’t think I can offer much advice.

  10. Pozinux says:

    It’s amazing thanks for this article !
    My problem is that I would like to be able to zoom in this image but I cannot do both at the same time… zoom and touch the clickable area. Do you have any hints for me ?

    • blahti says:

      I looked into different ways to do zooming here: Panning and Zooming.
      I have not tried to combine the that with clickable images. Let me know how that goes for you.

      • Pozinux says:

        Thanks for the quick reply ! I have seen your examples on zooming and the one on touching but I don’t understand how I can mix both of those examples… (I’m preety new to android). If I take this article source code, how can I had simple zoom functionnality ? Here is my OnCreate :

        @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ImageView iv = (ImageView) findViewById (R.id.image);
        if (iv != null) {
        iv.setOnTouchListener (this);
        }

        toast (“Touch the screen to discover where the regions are.”);

        // Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.p2_ship_default);
        // TouchImageView touch = new TouchImageView(this);
        // touch.setImageBitmap(bm);
        // touch.setMaxZoom(4f); //change the max level of zoom, default is 3f
        // setContentView(touch);
        }

        If I uncomment the second part, I can zoom but not touch specific parts anymore…

      • blahti says:

        Here is a suggestion. The Clickable Images example works because you have two images, one overlaying the other. The onTouch listener for that one handles hot spots and makes a click change the image being displayed. The Pan Zoom example that uses PanZoomListener (third one) also makes use of a touch listener. If you want some of the behavior from example 1 and some of the example 2, I think you have to find a way to come up with a new touch listener that can handle both.

        I would suggest starting by modifying the Clickable Images example so that it uses a touch listener subclass rather than calling a method of the Activity. Without changing example more than that, duplicate the existing behavior where the touch causes the image to change.

        Next start thinking about how to combine the two touch listener subclasses into one. Note that each one has its own way of handling touch actions. So the new class has to handle all the events without getting confused about whether it’s doing a pinch zoom or a simple touch. The key to the first example is that the overlay image fields the touch events. The key to the second example is that the touch events are the way it scales the views. So it seems like you have to have extra state variables in your new listener so it knows whether a pinch zoom is happening. When it is, do the zoom logic, but arrange to do it on both views. The Clickable Images demo had two views, but only one needed to have a touch listener. The new one you are doing will need to have both views with touch listeners. Scaling touch events have to be handled the same way for both of those views. Touch events that are just the simple clicks are handled only by the overlay image. Your onCreate method would make sure that both views got a new touch listener assigned to them.

        Generally speaking, with changes like this, I suggest doing it in several small steps. Use Log.d statements in your code to verify things are what they seem. Debugging touch events in the debugger itself is sometimes too confusing because there are so many events.

        I hope this helps you.

      • Pozinux says:

        Thanks blahti ! Your answer helps a lot. I understand the logic and principles of the solution but I already now that my knowledge of programming is too poor to make that happend in my new app unfortunetly… I thought it would be much easier to implement. Too bad for my project. But thanks again for your help. If one day you make a tutorial on mixing both tutorials (zooming and clicking on specific part) let me know ! 🙂

  11. I downloaded part of your source file such as the two .java file and all the .png file. It works on my Samsung ACE. Thank you very much. Kent Lau, Malaysia.

  12. Hainizam says:

    Hello sir. Lets say i want to do an apps that have 10 clickable images. How can i click on 3 or 4 images like checkbox? In your in example, i could click only one image. Btw thanks a lot.

    • blahti says:

      In this demo, when you touch a hot spot, a new drawable is brought into the ImageView with a call to setImageResource. You definitely don’t have to do that as your action. I did something simple so I did not have to spend a lot of time setting up images. The key point of this demo was to show a way to make it look like certain areas of a picture are clickable.

      I have one idea you might try. Start with your default image. Construct several images that would be placed on top of the default image. Each of those images would have the part of the image that changes showing and the rest of the image would be transparent. Stack up all your new images inside a FrameView, which makes them something like layers of the picture. All those layers are ImageView pbjects with visibility initially set to “invisible”. When a user touches a hotspot, have your code locate the correct ImageView and sets its visibility to “visible”. As long as your layer images do not overlap, it might have the effect you are after.

      To simplify things, you might start by triggering the changes with regular buttons, just to be sure that the images overlays are right. Then you could switch to driving off the hots spots.

      • Hainizam says:

        I understand what you are doing in this thread but the problem is, I do not know how to convert it to a different process. Lets say, I want to select the alien and the fire. However, the example u posted here show that the user could select only one image. And I dont really get it with the layer things.

        I’m very sorry, this is my first time developing an android games. Everything is still very blur to me. I really hope that someone could explain to me about the concept.

      • blahti says:

        Some suggestions:
        Figure out how a FrameView can be used how to stack one view on top of another. See http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html for an example.

        Then learn how to build an image with a transparent background. I do not do very elaborate graphics myself, but I know enough about Mac Paintbrush that I can create png files with transparent backgrounds. Just search: how to make transparent background.

        Then do a simple FrameView with a background image and the image with a transparent background.
        Then take what you know to build a more complex background image with several images on top.
        Once you are comfortable with that and can put it in a simple app, I think you will understand what I meant in my comments.

  13. Scott Funkhouser says:

    I am creating my first app ever so admittedly this is all pretty new to me. I am using eclipse and I imported the code from the website and integrated the parts I needed into my app. The app is not registering the hotspot events and as far as I can tell, by inserting log entries in various locations, the app isn’t even getting the evX and evY coordinates, let alone using the ColorTool. This app is just for my own personal use and mostly for learning purposes but it’s very frustrating that I can’t get it to work right. Here’s the code for my main activity
    package stuff.of.mine.golfapp;

    import android.os.Bundle;
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.Color;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;

    public class MainActivity extends Activity implements View.OnTouchListener{
    TextView textView1, textView2, textView3, textView4;

    int swingCount = 0;
    int hook = 0;
    int slice = 0;
    float goodSwing = 0;
    double accuracy = (goodSwing)/(swingCount);

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView1=
    (TextView) findViewById(R.id.textView1);
    textView2 =
    (TextView) findViewById(R.id.textView2);
    textView3 =
    (TextView) findViewById(R.id.textView3);
    textView4 =
    (TextView) findViewById(R.id.textView4);

    ImageView iv = (ImageView) findViewById (R.id.image);
    if (iv != null) {
    iv.setOnTouchListener (this);

    TextView textView1;
    textView1 =
    ((TextView) findViewById(R.id.textView1));
    textView1.setText(“Swings: ” + swingCount);

    TextView textView2;
    textView2 =
    ((TextView) findViewById(R.id.textView2));
    textView2.setText((“Hooks: “) + hook);

    TextView textView3;
    String test = String.format(“%.02f”, accuracy);
    textView3 =
    ((TextView) findViewById(R.id.textView3));
    textView3.setText(“Accuracy: ” + test + “%”);

    TextView textView4;
    textView4 =
    ((TextView) findViewById(R.id.textView4));
    textView4.setText(“Slices: ” + slice);
    }
    }

    public boolean onTouch (View v, MotionEvent ev)
    {
    boolean handledHere = false;

    final int action = ev.getAction();

    final int evX = (int) ev.getX();
    final int evY = (int) ev.getY();
    int nextImage = -1; // resource id of the next image to display

    // If we cannot find the imageView, return.
    ImageView imageView = (ImageView) v.findViewById (R.id.image);
    if (imageView == null) return false;

    Integer tagNum = (Integer) imageView.getTag ();
    int currentResource = (tagNum == null) ? R.drawable.range_default : tagNum.intValue ();

    switch (action) {
    case MotionEvent.ACTION_DOWN :
    if (currentResource == R.drawable.range_default) {
    nextImage = R.drawable.range_click;
    handledHere = true;

    /*
    } else if (currentResource != R.drawable.p2_ship_default) {
    nextImage = R.drawable.p2_ship_default;
    handledHere = true;
    */
    } else handledHere = true;
    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.
    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.range_default;

    if (ct.closeMatch (Color.RED, touchColor, tolerance)) {hook++; Log.d(“hook”, “hook”);}
    else if (ct.closeMatch (Color.YELLOW, touchColor, tolerance)) goodSwing++;
    else if (ct.closeMatch (Color.BLUE, touchColor, tolerance)) slice++;
    handledHere = true;
    break;

    default:
    handledHere = false;
    } // end switch

    if (handledHere) {

    if (nextImage > 0) {
    imageView.setImageResource (nextImage);
    imageView.setTag (nextImage);
    }
    }
    return handledHere;

    }
    public int getHotspotColor (int hotspotId, int x, int y) {
    ImageView img = (ImageView) findViewById (hotspotId);
    if (img == null) {
    Log.d (“ImageAreasActivity”, “Hot spot image not found”);
    return 0;
    } else {
    img.setDrawingCacheEnabled(true);
    Bitmap hotspots = Bitmap.createBitmap(img.getDrawingCache());
    if (hotspots == null) {
    Log.d (“ImageAreasActivity”, “Hot spot bitmap was not created”);
    return 0;
    } else {
    img.setDrawingCacheEnabled(false);
    return hotspots.getPixel(x, y);
    }
    }

    }

    protected void onSaveInstanceState(Bundle outState){
    super.onSaveInstanceState(outState);
    outState.putInt(“swingCount”, swingCount);
    outState.putInt(“hook”, hook);
    outState.putDouble(“accuracy”, accuracy);
    outState.putFloat(“goodSwing”, goodSwing);
    outState.putInt(“slice”, slice);

    }

    protected void onRestoreInstanceState(Bundle savedInstanceState){
    super.onRestoreInstanceState(savedInstanceState);
    slice=savedInstanceState.getInt(“slice”);
    hook=savedInstanceState.getInt(“hook”);
    swingCount=savedInstanceState.getInt(“swingCount”);
    accuracy=savedInstanceState.getDouble(“accuracy”);
    goodSwing=savedInstanceState.getFloat(“goodSwing”);
    }
    }

    • blahti says:

      One possibility is that you have not made the connection between the view and touch listener in the activity. The code above assumes it can locate the view with id “image”. Is there any chance that your layout file used a different id?

  14. Pingback: Zoom auf Teil eines Bildes - Android-Hilfe.de

  15. Ken says:

    Great blog! I gave this a try and have one question. It seems to work for 4 images but as a post mentioned above not sure how to do more than 4. My eclipse only registers the .color(red,green,blue). Am I missing something to get other .color(black, magenta, …) to work?

  16. Ken says:

    GOT IT TO WORK!!!! super thanks to you, cheers

  17. Excellent tutorial, very helpful, simple and easy to understand.

  18. I have created an app by combining ideas of image with clickable areas and pan/zoom listener for android. Now the image can be zoomed as well as clicked at the same time. I referred this article and this article (http://code.cheesydesign.com/?p=723) to create it. I have explained how I have achieved it in my blog (http://chathura2020.blogspot.com/2014/03/how-to-make-image-with-clickable-areas.html) with the source codes. I hope this will help to others as well.

  19. ARob33 says:

    Hello, I was wondering if I could apply this to an app that I want to make that would allow the user to click on parts of a guitar and do certain actions like make tablature. I would need ~144 hotspots (the number of frets x the number of strings (24×6=144) ) and they would all need to be different colors I assume. Is it even possible using this method? How would you approach this project? Many have reccomended this site as reference but I am having a hard time making it applicable to mine.

    Thanks,
    Aaron Robertson

    • Bill Lahti says:

      Yes. it should be possible but it would be very time consuming to set up for that many hot spots. I doubt that the other method (see the reference: http://catchthecows.com/?p=113) would be much easier. 144 is still a large number. For the method I used, you’d have to come up 144 distinct colors and have the code reliably tell the difference. So that’s one way.

      I wonder if you could get by with 6 colors, one for each of the strings. Then you’d know which string was touched and then use the x touch point to figure out which fret the player was on. If you chose two more unique colors and put them on the first fret and the last fret, you could do a scan of the image one time as that view went on the screen and you’d know how much room 24 frets takes on whatever device the app happens to be running on. Then you could calculate the fret number dynamically from the user touch. Of course, I made a few assumptions there, like the fret being laid our horizontally or vertically so you could find the ends. But it almost seems doable.

  20. Very useful. Thanks!

    I’ve based myself on it to make a clickable map from my region, using a conventional map as “main” image, and another copy of the map with the regions coloured as “mask” image. If the colours in the mask are different enough between them, works perfectly 🙂

  21. Kevin Hollingshead says:

    Really smart idea to use a hidden color mask! I’ve approached this either by defining touchable rectangular regions (left, top, right, bottom), or by labeling areas with a manually drawn touchable label. Can’t wait until I have a reason to do something with this!

  22. Chris Hinostroza says:

    Hello, wanna first of thank you very much for such a great tutorial.
    I’m a student at Florida Atlantic University and currently working on an application for a school project, I have experience with WPF , WIndows forms and console appliactions,but this is my first time coding an Android app, I pretty much are able to follow your tutorial up to the point where you use a ColorTool() Object not quite sure if I’m missing something obvious but i can’t find any references anywhere to what this method is where is located and how to use it, eclipse auto fill doesn’t help and I’m just stuck at this point, any help is appreciated thanks.

    • Bill Lahti says:

      There are two Java files in the folder. The main activity is in ImageAreasActivity.java and the tool is in ColorTool.java. I put it in a separate file in case I wanted to pull it out and use it somewhere else.

  23. ashwetha says:

    Hello,
    Is it possible to use the same color for the hotspots?If so,
    What changes should I make in the code??
    I tried using the same color but the problem what I faced is,
    if I click twice on the same spot,it will get counted and marked but my
    requirement is not to select the same spot once it is selected.Please suggest me a solution.

    • Bill Lahti says:

      Since this is a demo app, I simply added some code onTouch to show what you could do to match colors and find the hotspot and then take an action. In a real app, it might be better to have that code call some kind of onClickHotspot method and pass an integer id to identify which hotspot was clicked. With something like that in place, you could then arrange for two different hotspots to end up triggering the same action. In other words, I am suggesting that you do not try to use the same color twice. Stick with one color for each hotspot, and then do your special handling in an onClick method.

  24. antonio says:

    Hi

    Thanks you very much for this great tutorial.

    When I click on certain parts of my image I have an exception that is thrown in getHotspotColor method :
    “java.lang.IllegalArgumentException: y must be < bitmap.height()" or "java.lang.IllegalArgumentException: y must be < 0".

    This is my code :

    ////////////////////////////////////////////////////

    public int getRegionColor (int x, int y)
    {
    if(imgMapMaskColor != null)
    {
    imgMapMaskColor.setDrawingCacheEnabled(true);
    Bitmap bmp = Bitmap.createBitmap(imgMapMaskColor.getDrawingCache());
    imgMapMaskColor.setDrawingCacheEnabled(false);
    return bmp.getPixel(x, y);
    }
    return 0;
    }

    /////////////////////////////////////////////////

    Do you have a solution to this problem?
    Thank you in advance

    • Bill Lahti says:

      I have not seen this happen. What device are you running on? Which api level for Android?
      Does this behavior happen often? I wonder if it is only when the touch point is near the edge of the image.

      It might be good to insert some Log.d statements in that method to see what the values of x and y are when it blows up. Same thing, for the bitmap dimensions. A possible workaround would be to check x and y to see if they are in bounds. If not, substitute zero for x < 0 and the max value if x is greater than the max. At least then the method would not blow up.

      • antonio says:

        Thank you for your reply.

        The error occurs when I make a slide movement toward the edges of the image.

        I inserted several Log.d my method, and when I do the movement above describe the value is either negative or > to the size of Bitmap

      • Bill Lahti says:

        That means that the onTouch method needs to be a bit smarter. It should detect a swipe motion and not assume it is a simple click. One way of doing that is to use gesture detectors: http://developer.android.com/training/gestures/detector.html.
        If you do not want to use a gesture detector, you could have onTouch check that the x-y point is in bounds when ACTION_UP occurs. If it is in bounds, call getHotspotColor. Otherwise, it should ignore the event.

  25. antonio says:

    Here is my current method OnTouch

    //////////////////////////////////

    public boolean onTouch (View v, MotionEvent ev)
    {
    final int action = ev.getAction();
    final int evX = (int) ev.getX();
    final int evY = (int) ev.getY();

    if(imgMapMaskDefault.getId() == v.getId())
    {
    switch (action)
    {
    case MotionEvent.ACTION_UP:
    manageColorRegion(getRegionColor(evX, evY));
    break;
    }
    }
    return true;
    }

    //////////////////////////////////

    I manage only event ACTION_UP (is onClick ?) but the error is still present.

    • Bill Lahti says:

      In the UP clause, get the dimensions of the view. If evX or evY are out of bounds, do not call manageColorRegion.
      If x or y are out of bounds, it is because the user swiped down or left. That would not be a true touch, I would think.

  26. ujwalv says:

    Hi,Thanks for the tip on loading different image resource and ColorTool for getting its X Y rather than work on the top image, I used slightly similar trick but instead did the recursion around the pixel where clicked happened looking for the expected color in X+ X- Y+ Y- axis and getting its color,since my clickable area was a line .
    Thank you

  27. Fah Kimhajan says:

    Thank you for your sharing. I have problems with currentResource and ColorTool. I would like to check with your source code file but I cannot download from the link that you provide. Could you please check?

    • Bill Lahti says:

      Sorry,something has changed with the Google doc viewer. Use the download icon on the wglxy.com page for the demo app. The download icon is to the right of the date and the version number. That one works fine.

  28. Martin says:

    Hello, I’m quite green when it comes to Android applications. I followed your guide and it helped me a lot when it comes to understanding how everything works. At the moment I’m making an application that would require making clickable map areas, where each click on a different area would start a new activity. Any ideas? Best Regards, Martin

    • Bill Lahti says:

      I wrote several articles that might help you think about starting activities: (1) activity lifecycle; (2) Dashboard demo; (3) fragments demo, in case you want to make use of fragments, rather than activities. As the Dashboard demo shows, sometimes you want start the new activity and leave the prior activities going (so the Back button works) and other times, you want to remove them all. That’s worth thinking about as you structure your app.

  29. rishabhbanga says:

    Not able to download from that site.

    • Bill Lahti says:

      If you are viewing the page in Chrome, click the download icon at the bottom of the page to download. If you browse to the zip file first, you will find yourself on a page where the download button does not work. (It is a Google or Chrome problem, as far as I can tell.)

  30. Nira Li says:

    Thankyou so much for sharing this,
    after i try to implement all your code, im sure i write the right code. But strangely it just keep change to ‘pressed’ image whether i click the right colour part in screen or just the white area…
    any ideas?

    • Bill Lahti says:

      I do not have any ideas at the moment, but I will try the demo app again on a newer version of Android, just in case something has changed in Android.

      • Bill Lahti says:

        Everything works just fine in the demo app, even on the newest Android (5).

        I would suggest checking the ids you use in your layouts for the images and make sure they match what you have in your code. The demo has a line like “ImageView imageView = (ImageView) v.findViewById (R.id.image);” in the onTouch method. That’s followed by a line “if (imageView == null) return false;”. So if your names don’t match, nothing will happen in onTouch. Similarly, in onCreate, if the names do not match, you will not get a touch listener attached correctly.

        Other than that, I would suggest breakpoints in the code and adding a few Log.d statements so you can see how far you get after a touch before things go amiss.

      • Nira Li says:

        wow thankyou for your fast response, really appreciate that 😀
        i’ve tried to make in another project exactly like what u did, and i carefully look into id and things like you said. yah your project is works.
        but if i change your image to another pic, it just become strange. like i said before.
        i already make new image with ‘mask image’ exactly same size. i there anything i’ve miss?
        i make mask picture with photoshop, just size the paper same like the main picture i use, then i make the ‘hotspot area’, and last i change the background colour into white. is there something wrong? or maybe the way i make the mask? i just create into png.
        but im sure the code was fine. the color match just wont work. it just detect all area in my mask picture with white color.
        im sorry for asking too much. but i need to learn this for my school project 😦

      • Nira Li says:

        Its work! thank you so much. i dont know why, but i tried to clean project, then suddenly its work. hahahahahahahaha
        my mistake. but thanks a lot for the tutorial and clearly explanation.

  31. Sabri says:

    Thank you 🙂

  32. Harry says:

    Doesn’t seem able to download in anyway, whether from Safari or Chrome, and whether using the custom Download button or standard Google Doc download arrow. Any idea?

  33. Pedram says:

    Hi
    Download link doesn’t work. I was wondering if you could send me the code

  34. Varsha says:

    Hi. Thank you for sharing this and explaining it so neatly. I was able to download your code & it is working perfect.
    I have a requirement in my app to highlight portion of image we touch. For example, in a apartment floor plan image when you click on particular room that room should get highlighted. Can you help me with that?
    Thanks in advance.

    • Bill Lahti says:

      I think a lot depends on where the floor plan image comes from. If it originates with the app, you can use the techniques described in this article. You’d have to create an overlay image from the original floor plan for each of the rooms. That would be like my sample program, where there are six images with the starship in it. That’s certainly possible, but might lead to a lot of space being used for images, if you had a lot of rooms. And that approach would not work at all if the floor plan came directly from a user, unless you asked users to upload their floor plan for processing, and some time later, after you manually created the images with rooms highlighterd, their floor plan was available for use.

      You might be better off modeling floor plans as objects made up of rooms. Each room would have its own view that could support highlighting on touch actions. This approach would likely be pretty complicated unless your floor plans were simple enough that that they could be handled with a grid view or RelativeLayout views.

  35. Pingback: Guess picture game – image overlays, hot regions (android) | news

  36. Hi, I created an android library that lets you define clickable areas in an image, associate objects to it and listen for touch events. Maybe someone likes to check it out!

    It’s on Github: [ClickableAreasImages][1]

    [1]: https://github.com/Lukle/ClickableAreasImages

  37. Aga Francis says:

    Hi, Thank You for this tutorial you helped me a lot sir Blahti
    But I am experiencing an odd bug

    I tried this on different phones and screen sizes
    On my phone it is working perfectly but on my friend’s phone it doesn’t
    so I tried to capture their values

    Color = -612354 (Something like that)
    TouchColor = 0

    next I put the colormap into visible
    but it fits the supposedly clickable area

    What could be the problem?

    • Bill Lahti says:

      In hex, that number is FFFFFFFFFFF6A7FE. Does that seem to be the color near the touch point? Or is the touch color really black? If the touch color is really black, check overlay images.

      I would check that your image and your overlay images are exactly the same size, and make sure that those two images are in all of the right drawable folders. For example, if you have the real image in the drawable-hdpi folder, there should be an overlay image there also. If you were to leave out the overlay image from one of the folders, that could explain what’s going on.

      The check in the original code uses the difference of absolute values. Seeing zero at the touch point makes me wonder about that value. Does your overlay image use white as the background color? Black? Or a value with the alpha value set?

      It’s always advisable to check versions of Android, makes of phones, screen sizes, etc. There might be something there that might help explain what is going on.

  38. Ram says:

    Hi,

    Can I also link URL to open on click of an area. Something similar to imagemap in html?

    • Bill Lahti says:

      Yes, you can make the action be whatever you want it to be. If you wanted to do a link, the touch action might look something like this:

      url = res.getString (R.string.my_test_url);
      Intent i = new Intent(Intent.ACTION_VIEW);
      i.setData (Uri.parse(url));
      startActivity(i);

  39. Caroline says:

    Hi. Thank you for posting this tutorial! I am having an issue where the error “Cannot resolve method ‘getHotspotColor (int, int, int)'” comes up. Do I need to import anything?

  40. James says:

    This is really good. Thankyou. Works really well.
    I shortened your code to suit my needs. Hope this helps someone.

    // mDrawerImageView is the image
    // mDrawerOverlayView is the image which I have drawn over in red using GIMP (or mspaint)

    mDrawerImageView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

    //user selects the bodyPart from the colours in the hidden mDrawerOverlayView
    if (event.getAction() == MotionEvent.ACTION_DOWN) {

    //get colour
    mDrawerOverlayView.setDrawingCacheEnabled(true);
    Bitmap hotspots = Bitmap.createBitmap(mDrawerOverlayView.getDrawingCache());
    mDrawerOverlayView.setDrawingCacheEnabled(false);
    int selectedColour = hotspots.getPixel((int)event.getX(), (int)event.getY());
    int tolerance = 25;

    //face
    if ((Math.abs (Color.red (Color.RED) – Color.red (selectedColour)) < tolerance)) {
    Log.d(TAG, " :::::::::::::::: FACE ::::::::::::::::");
    }

    //leg (etc)

  41. Pingback: Vườn Tâm Hồn – Mobile Apps & Tips Blog

  42. shashank pant says:

    Nice explanation.But i want the image to be clicked from many regions and i have the pixel position of the region.How can can i achieve my goal which is when i touch on particular pixels which i am having the event will happen, also how can i save those pixels values in android drawable to compare them when i click on a particular region could you please explain this?

    • Bill Lahti says:

      I am bit unclear about what you are trying to do. It sounds like you are going to have the same image displayed multiple times on the screen, like maybe in view V1, view V2, view V3, etc. And you’d like the action taken when a click is in V1 to be different than an action in V2, and so on. If that’s the case, note that the onTouch method takes the View as an argument. If you assign different view ids to each view, you could make the first action in onTouch to check the view id and call another method. Something like onTouchV1, onTouchV2, etc. The logic in each of those would be similar to my onTouch in the example, though you would change the actions to whatever you need to do in each of the views.

      Hope I interpreted your question correctly? If not rephrase, and ask again.

  43. sema says:

    can i user more than only three colors? want to use more than 15 colors.

    • Bill Lahti says:

      Yes. Update the closeMatch method so it looks for the colors you want.

      • seema says:

        unable to use my custom colors, I want to put a color.xml file and mask my image view with those colors to create click listener , please guide.

      • Bill Lahti says:

        Be sure that the 16 colors you use are not very similar. Because of the way colors are done on the screen, one cannot guarantee that the pixel where the touch is made will have exactly the same color values as what you assigned. That’s why there is a close match method in the code.
        If you are having trouble with your own code, start with the working example and modify it, one color at a time, to add the colors you want to use. That might help you determine if you have the color matching problem.
        If you don’t think colors are problems, I’d suggest putting in a breakpoint in the touch handling code and walking through the code in the debugger.

  44. Calvin Castle says:

    Hey, first off great tutorial. I was wondering if it is possible to display multiple parts of the image at once. Such as the user clicks the alien and then the flames (both are displayed), because currently it can only display one and then the other.

    • Bill Lahti says:

      The action taken on touch is under your control. I think you could do what you want pretty easily. Build an image that has the two images (alien, flames) both showing. Have that one be the one that is displayed when the user touches the right area of the image.

  45. Nancy says:

    Hey, I have applied your solution in one of my apps. But I have a problem when i zoom the image and click on the hotspot it doesn’t give me the exact color rather it give me white color. Could you please help me find a solution to it?

    • Bill Lahti says:

      I think you have to keep track of how much you have zoomed the image. If you know the zoom factor, you can scale the touch point x and y points to match. Then when you call getHotSpotColor, you should get the correct color.

  46. Pingback: Implementing Clickable Images – Pocket Science Lab

  47. Pingback: Android: Placing button relative to ImageView - Tutorial Guruji

  48. Pingback: Make multiple clickacble areas in an image - Tutorial Guruji

  49. Pingback: Make multiple clickacble areas in an image

Leave a reply to Ken Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.