Question

I'm trying to implement four custom shaped buttons like you can see in the following picture:

enter image description here

What I did so far: I took 4 different pictures - each with only one color visible (see above). The other part of the image is transparent. This has the result, that I have four pictures with the same size.

Now I used a relative-layout where all my 4 pictures are added into imageviews on the same position. Because of the transparency, I can see the desired picture.

For my ImageViews I've implemented onTouchListener with the following content:

private class ImageOnTouchListener implements View.OnTouchListener {
    private int categoryId;

    public ImageOnTouchListener(int categoryId) {
        this.categoryId = categoryId;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Bitmap bmp = Bitmap.createBitmap(v.getDrawingCache());
        int x = (int) event.getX();
        int y = (int) event.getY();
        boolean isInsideBitmap = x < bmp.getWidth() && y < bmp.getHeight() && x >= 0 && y >= 0;
        boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;
        if (isInsideBitmap) {
            int color = bmp.getPixel(x, y);
            bmp.recycle();
            if (color == Color.TRANSPARENT){
                return false;
            }
            else {
                if (isActionUp) {
                    buttonClick();
                }
            }
        }else{
            bmp.recycle();
        }
        return true;
    }
}

This approach works but it consumes a lot of memory as I'm always creating a bitmap when I move my finger. I'm not quite sure if this is the best way to implement this. Is there anything I can do different which might result in a more efficient way?

Was it helpful?

Solution

Using the fact that you can tell whether or not coordinates belong in a circle with the equation x² + y² <= radius² when the circle's center is (0, 0), I think the following should work.

public class ImageOnTouchListener implements View.OnTouchListener {

    // TODO  Adjust this value
    private static int QUADRANT_RADIUS = 100; // in pixels
    // TODO  Adjust this value
    private static int SPACE_BETWEEN_QUADRANTS = 5; // in pixels

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int relativeX = (int) (event.getX() - v.getX());
        int relativeY = (int) (event.getY() - v.getY());
        int center = QUADRANT_RADIUS + (SPACE_BETWEEN_QUADRANTS / 2);

        boolean isInsideCircle = Math.pow(relativeX - center, 2) + Math.pow(relativeY - center, 2) <= Math.pow(center, 2);

        boolean isInsideBottomLeftQuadrant = isInsideCircle &&
                relativeX <= QUADRANT_RADIUS &&
                relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
        boolean isInsideBottomRightQuadrant = isInsideCircle &&
                relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
                relativeY >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS;
        boolean isInsideTopLeftQuadrant = isInsideCircle &&
                relativeX <= QUADRANT_RADIUS &&
                relativeY <= QUADRANT_RADIUS;
        boolean isInsideTopRightQuadrant = isInsideCircle &&
                relativeX >= QUADRANT_RADIUS + SPACE_BETWEEN_QUADRANTS &&
                relativeY <= QUADRANT_RADIUS;

        boolean isActionUp = event.getAction() == MotionEvent.ACTION_UP;

        if (isActionUp) {
            if (isInsideBottomLeftQuadrant) {
                // Handle bottom left quadrant click
                buttonClick();
                return true;
            } else if (isInsideBottomRightQuadrant) {
                // Handle bottom right quadrant click
            } // etc.
        }

        return false;
    }
}

You'll need to adjust the QUADRANT_RADIUS and SPACE_BETWEEN_QUADRANTS values, and then either handle all touch events from a single view (like my snippet does) or have a slightly different touch listener per image.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top