sngreco
sngreco

Reputation: 1148

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:

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;
}

Upvotes: 18

Views: 8496

Answers (3)

Asad
Asad

Reputation: 1439

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
    }

Upvotes: 1

aniki.kvn
aniki.kvn

Reputation: 687

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;
        }
    }
};

Upvotes: 39

iseeall
iseeall

Reputation: 3431

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.

Upvotes: 3

Related Questions