5

In my Android Application, I have three irregular shape across globe as below :

enter image description here

So I placed this whole image in layout.Globe is just an image .There is no role of globe here. Now I want to place one buttons on top of the each irregular shaped images which are across the globe.

Button is always rectangular but I want to get touch event for irregular shape,not for rectangular area. So How can I put three buttons on these three irregular shape or is there any solution? so that different event for each button can be handled in activity?

I don't have idea on how to achieve this task. Please guide me on this.

Any advice or suggestions ?

Any help will be appreciated.

Ponting
  • 2,248
  • 8
  • 33
  • 61
  • get the touch positions ! http://stackoverflow.com/questions/3476779/how-to-get-the-touch-position-in-android – Rachit Mishra Aug 25 '13 at 20:05
  • 1
    This will help you: http://stackoverflow.com/questions/11283653/real-custom-shape-of-button Also you can divide your layout into virtual segments/arcs and handle touch events. – JiTHiN Aug 28 '13 at 14:33
  • the globe is static or dynamic ? – Rachit Mishra Aug 28 '13 at 14:48
  • @twntee : It is static.Globe is just an image .There is no role of globe here. – Ponting Aug 28 '13 at 14:51
  • @twntee : I can get touch position as you posted thread but what if screen size changed by using another device? Then Entire calculation will be go in vain. – Ponting Aug 28 '13 at 15:04
  • 1
    http://stackoverflow.com/a/8086317/826657 you saw this ? – Rachit Mishra Aug 28 '13 at 15:10
  • wait, i add it as answer you accept :) if you will to :) – Rachit Mishra Aug 29 '13 at 09:48
  • @twntee : Thread in your Comment is not perfect answer.But from this thread http://stackoverflow.com/questions/11283653/real-custom-shape-of-button I got the answer.Your comment gave me hint.But it helps me. – Ponting Aug 29 '13 at 09:52
  • 1
    all your wish :), i am happy at least i was of some help :) – Rachit Mishra Aug 29 '13 at 09:56
  • @twntee : Thank your for help.Without your hint I can't reach at this point.:) – Ponting Aug 29 '13 at 10:11
  • well, as I think all credit goes to you. – Rachit Mishra Aug 29 '13 at 10:19
  • Oh really!! Half to you and half to me.Now fine ? – Ponting Aug 29 '13 at 10:38
  • @Ponting post your solution as an answer and accept it, it may help others in future. – Rachit Mishra Aug 29 '13 at 19:12
  • @twntee : Sure,I will post solution at end of Bounty. For that period I am looking for whether better solution is available ? – Ponting Aug 30 '13 at 06:19
  • Well, you have half a pizza composed of 3 slices, and you want to detect the touches in the outer arc. It is pretty simple geometry and you can detect in what piece the touch is (using the angle it makes with the center) and whether the touch is in the outer arc or not (using the distance from the center). I do not this this problem needs pixel checking especially that coordinates approach allows for some fuzziness for touches if required. – Sherif elKhatib Sep 03 '13 at 18:38

4 Answers4

1

I haven't tested this, but I think it will work. I'm sure you'll have to tweak it and make corrections.

Create a copy of your background image where the three buttons are three distinct colors with no aliasing, and all other pixels are pure black. It needs to be a PNG so it won't have JPG compression artifacts. This will be your mask. You can just use pure red, green, and blue since you have only three buttons. It can probably be lower resolution than your original image, as long as the aspect ratio is the same.

Subclass ImageView and use your subclass instead for drawing the background. Pass your subclassed version of ImageView your mask image as a Bitmap.

class TouchMaskImageView extends ImageView {
    ...constructors

    public void setTouchMask(Bitmap mask){
        this.mMask = mask;
    }

    public interface OnTouchedListener{
        public void onTouched(MotionEvent event, int colorTouched);
        public void onTouchCanceled();
    }

    public void setOnTouchedListener(OnTouchedListener listener){
        this.mOnTouchedListener = listener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (mOnTouchedListener  != null && mMask != null ) {
            if (x >=0 && x < (float)getWidth() &&
                y >=0 && y < (float)getHeight()) {
                //Next two lines will be more complicated if 
                  // you aren't using scaleType fitXY
                int maskX = (int) x * mMask.getWidth() / getWidth();
                int maskY = (int) y * mMask.getWidth() / getHeight();
                int maskColor = mask.getPixel(maskX, maskY);
                mOnTouchedListener.onTouched(event, maskColor);
            } else if (event.getAction()==MotionEvent.UP){
                //released finger outside image view
                mOnTouchedListener.onTouchCanceled();
            }
        }
    }
}

Now you can create an OnTouchedListener that handles touch logic similar to a button. It will need to track states. Here's an example, but I'm not sure if this will break when there are multi-touches. You may need to also set an action mask for the first finger and use getActionMasked() instead of getAction().

public void onTouched(MotionEvent event, int colorTouched){
    switch(mState){
    case STATE_WAITING:
        if (event.getAction()==MotionEvent.ACTION_DOWN){
            switch (colorTouched){
            case 0xffff0000: //red
                mTouchMaskImageView.setImageResource(R.drawable.redButtonDown);
                mState = STATE_LATCHED_RED;
                break;
            case 0xff00ff00: //green
                mTouchMaskImageView.setImageResource(R.drawable.greenButtonDown);
                mState = STATE_LATCHED_GREEN;
                break;
            case 0xff0000ff: //blue
                mTouchMaskImageView.setImageResource(R.drawable.blueButtonDown);
                mState = STATE_LATCHED_BLUE;
                break;
            }
        }
        break;
   case STATE_LATCHED_RED:
        switch (event.getAction()){
        case (MotionEvent.ACTION_MOVE):
            if (colorTouched = 0xffff0000)
                mTouchMaskImageView.setImageResource(R.drawable.redButtonDown);
            else
                mTouchMaskImageView.setImageResource(R.drawable.defaultImage);
            break;
        case (MotionEvent.ACTION_UP)
            if (colorTouched = 0xffff0000)
                performRedButtonAction();
            mTouchMaskImageView.setImageResource(R.drawable.defaultImage);
            mState = STATE_WAITING;
            break;
        }
        break;
   case etc. for other two latched colors...
        break;
   }
}

public void onTouchCanceled(){
    mTouchMaskImageView.setImageResource(R.drawable.defaultImage);
    mState = STATE_WAITING;
}

This is somewhat crude, since you would need four different copies of your image to represent the four possible states. If you want to put the time in to save some memory and make it run faster, you could try to set up your layout with separate image views for the three buttons overlayed in the correct positions and target those for the image changes. But this would be very challenging, considering you would have to make it line up perfectly for any screen size.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Dude,This seems like very high level concept.I am just naive developer .Thanks for answer.This thread http://stackoverflow.com/questions/11283653/real-custom-shape-of-button solves my problem. – Ponting Aug 29 '13 at 09:38
  • That solution is easier, but you'll still need to do something similar to my second block of code to get it to behave like a button (button image changes when you press down on it, and you can cancel the button press by dragging your finger off before releasing). Are you going to use separate image views for each button? – Tenfour04 Aug 29 '13 at 12:04
  • Yes.I am using separate image view. – Ponting Aug 29 '13 at 12:18
0

The FLAG_WINDOW_IS_OBSCURED flag has worked well for me in the past but it may take some working with to get right. It may be worth a try but to be honest I'm still not sure exactly how it determines what is and isn't obscured, so it may not be applicable here.

static OnTouchListener listenerMotionEvent = new OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if ( (motionEvent.getFlags() | MotionEvent.FLAG_WINDOW_IS_OBSCURED) != MotionEvent.FLAG_WINDOW_IS_OBSCURED ) {
            return false;
            // View is not obscured so we pass on the event
        }

        // Process the motion event here and return true if you consume it
};
Jon
  • 1,398
  • 9
  • 14
0

Would slicing an image in Photoshop, then placing that image in a webview help?

CAM-Dev
  • 186
  • 3
0

From above answers,I got hint and finally I got Solution as below:

What I did is that :

Used RelativeLayout in xml file

Used ImageView, as below :

  <ImageView
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/glob2"
        android:layout_marginBottom="24dp"
        android:layout_marginLeft="80.5dp"
        android:contentDescription="@string/profile_picture_description"
        android:src="@drawable/pick_matches_background" />

Here I set the margin accordingly(left/right/bottom/top).

In activity,

Declare

Bitmap btmp; and
ImageView img;

In onCreate Method,

 // initialized image view
img = (ImageView) findViewById(R.id.btn1);
img.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

       // call your method here

  }
});

StateListDrawable sdCreateTrip = (StateListDrawable) img
            .getDrawable();

btmp = ((BitmapDrawable) sdCreateTrip.getCurrent())
            .getBitmap();

You are ready to go now.

Ponting
  • 2,248
  • 8
  • 33
  • 61
  • Looks like your margins are using hardcoded values, how does that work on different screen sizes? – Mike Sep 25 '13 at 07:59