1

I'm trying to create an app that has a fairly complex UI. I'm trying to understand what is the best practice to render irregular button shapes to the screen.

I'm adding this image as an example. Each one of these wooden boards is a button. I obviously can't use Android's Button or ImageButton because the shape is not a rectangle.

I assume I need to draw this directly onto a canvas or use onDraw or Draw to make this happen. Is this the correct way I should use to render these as buttons? Any good reading material on these is HIGHLY appreciated..

Thank you

enter image description here

Alon Minski
  • 1,571
  • 2
  • 19
  • 32
  • 2
    I think you are right, it's better to not use Buttons for that - it should be your own customized control for all 3 items and processing touch events to decide which one is pressed. – Mixaz Apr 05 '15 at 09:17
  • 1
    A code sample: http://stackoverflow.com/questions/6756806/android-drawing-on-touch-event – Mixaz Apr 05 '15 at 09:21
  • Thank you for the reply! So it seems. He's loading the bitmap, uses onDraw to draw it and onTouchEvent to handle touch events. So in essence, the bounding box around the bitmap will follow the pixels in the bitmap? Or is it a rectangle bounding box and areas that have transparency will also be areas that fire touch events? – Alon Minski Apr 05 '15 at 10:09
  • 1
    bounding box is always a rectangle, and in your case it will be for whole view. I suggest you use approach described here http://stackoverflow.com/a/8086317/1028256 but check the touched pixel against 3 bitmaps, considering that the images have transparency areas for other buttons – Mixaz Apr 05 '15 at 10:53
  • Ahh.. so getPixel is the method I need to use to make sure "hot zone", or clicked area is not a transparent pixel. So basically, I can create a custom view, that loads a given bitmap, and the onTouch method will keep checking if the touched area has pixels with getPixels. If it has, fire touch event, don't do anything otherwise. That's the basic idea correct? Arranging the "wooden boards" will be done in a regular straight way correct? Meaning positioning the custom views inside a RelativeLayout for instance. And hoping it will look roughly the same as the specs at the end? :) – Alon Minski Apr 05 '15 at 11:09
  • 1
    yes, with following correction: I would load 3 bitmaps each representing single button (where 2 others are transparent), in `onDraw()` I would paint those 3 bitmaps, and in `onTouch()` I would check the touch event against them to see which bitmap/button was clicked. And it would be within one custom view, not 3 as you described. – Mixaz Apr 05 '15 at 12:34
  • Brilliant! This will keep the original design much much better. Thank you! :) – Alon Minski Apr 05 '15 at 12:48
  • yeah, in games it's better to use your own user input handling, instead of system UI controls, or you spend to much time trying to make them work in non-standard conditions )) I'll submit an answer then – Mixaz Apr 05 '15 at 13:10
  • I see. Yes please do. If there's any code example for your previous answer to clarify it more that would be perfect! Thanks again. – Alon Minski Apr 05 '15 at 13:12

1 Answers1

1

You can create a customized View working in following way:

  1. in onDraw() paint 3 bitmaps each representing single button (where 2 other buttons are transparent),
  2. in onTouch() check the touched pixel against those bitmaps to see which bitmap was clicked

Code snippet:

public class DrawingBoard extends View {

    Bitmap mBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.button1);
    Bitmap mBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.button2);
    Bitmap mBitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.button3);

    public DrawingBoard (Context context) {
        // TODO Auto-generated constructor stub
        super (context);            
    }
    @Override
    protected void onDraw (Canvas canvas) {
        canvas.drawBitmap(mBitmap1, 0, 0, null);
        canvas.drawBitmap(mBitmap2, 0, 0, null);
        canvas.drawBitmap(mBitmap3, 0, 0, null);
    }
    @Override
    public boolean onTouchEvent (MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN : 
                int xx = (int)event.getX();
                int yy = (int)event.getY();
                if(Color.alpha(mBitmap1.getPixel(xx,yy)) != 0) {
                    // button1 pressed
                }
                else if(Color.alpha(mBitmap2.getPixel(xx,yy)) != 0) {
                    // button2 pressed
                }
                else if(Color.alpha(mBitmap3.getPixel(xx,yy)) != 0) {
                    // button3 pressed
                }
                break;
        }

        return true;

    }
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
     // bitmaps are assumed to be of the same size
     setMeasuredDimension(mBitmap1.getWidth(),mBitmap1.getHeight());
    }    
}

I didn't test the code, it it may have errors.

A variant - you can create a virtual bitmap storing 'hit codes' for pixels on whole image. You can create it from original picture but replace pixels with ids to detect which area was touched, all other pixels make 'empty' (0x0). So getPixel() will return id of a button.

Mixaz
  • 4,068
  • 1
  • 29
  • 55