How to prevent onClick method on transparent portion of a PNG-loaded ImageView

I am currently developing an Android app that displays multiple images (as ImageView's) stacked on top of each other. Here is how the layers are currently configured:

  • Background layer: scales the entire screen, must be clickable
  • Foreground layer: scales the entire screen, must be clickable, contains transparency which allows the user to see some of the background layer

The problem I face is with the foreground layer. I am assigning the onClick() method to the imageview, but the method is being called whether they hit the portion of the image which is visible as well as the part which contains transparency. I only want the foreground ImageView onClick() method to be called when the user clicks a portion of that imageview that is not transparent.

This is what the scenario looks like:

enter image description here

The diagonal lines represent the transparent portion of the Foreground image. If a user touches this space, I want it to access the Background image instead of the Foreground image. Thank you for any assistance you can provide.

Here is the solution I implemented (Thanks to answer below):

//ontouchlistener - gets X and Y from event
private void setClick(View view)
{
    view.setOnTouchListener(new View.OnTouchListener() 
    {
        public boolean onTouch(View v, MotionEvent event) 
        {
            int imageId = getImageId((int)event.getX(), (int)event.getY());
            if (imageId >= 0)
                performActions(imageId);
            return false;
        }
    });
}

//get the ID of the first imageview (starting from foreground, 
//working backwards) which contains a non-transparent pixel
private int getImageId(int x, int y)
{
    ViewGroup parent = (ViewGroup) findViewById(R.id.relative_layout);
    for (int a = parent.getChildCount()-1; a >= 0; a--)
    {
        if (parent.getChildAt(a) instanceof ImageView)
            if (!checkPixelTransparent((ImageView)parent.getChildAt(a), x, y))
                return parent.getChildAt(a).getId();
    }
    return -1;
}

//get bitmap from imageview, get pixel from x, y coord
//check if pixel is transparent
private boolean checkPixelTransparent(ImageView iv, int x, int y)
{
    Bitmap bitmap = ((BitmapDrawable) iv.getDrawable()).getBitmap();
    if (Color.alpha(bitmap.getPixel(x, y)) == 0)
        return true;
    else
        return false;
}

Solution 1:

This one sample makes ImageView's transparent area not clickable.

ImageView:

ImageView imgView= (ImageView) findViewById(R.id.color_blue);
imgView.setDrawingCacheEnabled(true);
imgView.setOnTouchListener(changeColorListener);

OnTouchListener:

private final OnTouchListener changeColorListener = new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
        int color = bmp.getPixel((int) event.getX(), (int) event.getY());
        if (color == Color.TRANSPARENT)
            return false;
        else {
            //code to execute
            return true;
        }
    }
};

Solution 2:

If your foreground image is not just a rect but a complex image and you really need that the touch is pixel-precise, you may use

http://developer.android.com/reference/android/view/View.OnTouchListener.html

foregroundImage.setOnTouchListener(new View.OnTouchListener(){...});

The MotionEvent in the callback will contain what kind of action happened (e.g. Touch up) and the exact location.

If you know the exact size of the foreground image as it is displayed, you can figure out which pixel of it was clicked, then check if that pixel's alpha is 0. Or you may need to apply some scaling if the image was scaled. This may get quite tricky since depending on the screen size and proportions the image may have been scaled/positioned differently. This also depends on the layouts your were using.

For the check of the pixel value you'd probably need to keep in memory the Bitmap object containing your foreground's image data as well.

Frankly, I doubt you'd really need all that precision unless your foreground image is really of a very irregular shape.

Solution 3:

Bitmap.createBitmap(v.getDrawingCache() and imageView.setDrawingCacheEnabled(true) are depreciated so you can do this by the following snippet code:

imageView.setOnTouchListener { v, event ->
            val bmp = convertViewToDrawable(v)
            val color: Int = bmp.getPixel(event.x.toInt(), event.y.toInt())
            if (color == Color.TRANSPARENT)
                return@setOnTouchListener false
            else {
                Toast.makeText(baseContext, "image clicked", Toast.LENGTH_SHORT).show()
                return@setOnTouchListener true
            }
        }

private fun convertViewToDrawable(view: View): Bitmap {
        val b = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight,
            Bitmap.Config.ARGB_8888)
        val c = Canvas(b)
        c.translate((-view.scrollX).toFloat(), (-view.scrollY).toFloat())
        view.draw(c)
        return b
    }