17

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;
}
sngreco
  • 1,138
  • 11
  • 16
  • 2
    try not to have a transparent part. crop the image to remove the transparent parts if you can – njzk2 Mar 11 '13 at 16:11
  • thank you for your comment, but some of the foreground images are very complex, and are unable to be cropped – sngreco Mar 12 '13 at 15:46
  • then you have to look into the pixels of the image and compare to transparent – njzk2 Mar 12 '13 at 16:47
  • @JeremiahGreco, did you solve your issue? I tried your code but its not working properly. When you go in depth to get color of stacked images it returns different color every time. Can you help me please? – Nauman Zubair Aug 06 '14 at 14:31

3 Answers3

39

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;
        }
    }
};
aniki.kvn
  • 687
  • 7
  • 15
  • 1
    I know this is old, but this is a very good answer. You have helped me out so much! – Phil3992 Jan 28 '15 at 18:11
  • 3
    If you want to use it as an OnClick, you should update //code to execute by if(event.getAction()==MotionEvent.ACTION_UP){/*code to execute*/}. – Mateu May 21 '15 at 09:45
  • 2
    i had to add v.buildDrawingCache(), if not a npe is raised – Borja Jun 13 '16 at 09:34
  • This is one of the best answers I've seen on the site! Thank you! Spent the last 2.5 hours figuring this out!!! – Sreehari R Dec 25 '17 at 09:42
3

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.

iseeall
  • 3,381
  • 9
  • 34
  • 43
  • Let me see if I understand - Use OnTouchListener to determine the selected pixel, and then check the alpha of the pixel in all of the images starting from front to back – sngreco Mar 12 '13 at 14:10
  • thank you for the help iseeall, i updated my question with the solution i built from your answer – sngreco Mar 12 '13 at 23:03
1

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
    }
Asad
  • 1,241
  • 3
  • 19
  • 32