Question

I am developing a simple app that produced bubbles on screen on touch. Bubble move around on the screen and get popped when it reaches the border of screen or if a user touches it. I have successfully coded a bubble to pop when reaches borders of the screen but can't figure out a way to detect if the user touched it.

I want to detect if the user touched any bubble on the screen.

Note:- The bubbles are created using custom view. Also I have included some important functions only but can include whole code if you want. Here's the code

public class BubbleActivity extends Activity {

// These variables are for testing purposes, do not modify
private final static int RANDOM = 0;
private final static int SINGLE = 1;
private final static int STILL = 2;
private static int speedMode = RANDOM;

private static final int MENU_STILL = Menu.FIRST;
private static final int MENU_SINGLE_SPEED = Menu.FIRST + 1;
private static final int MENU_RANDOM_SPEED = Menu.FIRST + 2;

private static final String TAG = "Lab-Graphics";

// Main view
private RelativeLayout mFrame;

// Bubble image
private Bitmap mBitmap;

// Display dimensions
private int mDisplayWidth, mDisplayHeight;

// Sound variables

// AudioManager
private AudioManager mAudioManager;
// SoundPool
private SoundPool mSoundPool;
// ID for the bubble popping sound
private int mSoundID;
// Audio volume
private float mStreamVolume;

// Gesture Detector
private GestureDetector mGestureDetector;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    // Set up user interface
    mFrame = (RelativeLayout) findViewById(R.id.frame);

    // Load basic bubble Bitmap
    mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b64);

}

@Override
protected void onResume() {
    super.onResume();

    // Manage bubble popping sound
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {

        // Get the size of the display so this view knows where borders are
        mDisplayWidth = mFrame.getWidth();
        mDisplayHeight = mFrame.getHeight();

    }
}

// Set up GestureDetector
private void setupGestureDetector() {

    mGestureDetector = new GestureDetector(this,

            new GestureDetector.SimpleOnGestureListener() {

                // Detecting if user touched bubble here

                @Override
                public boolean onSingleTapConfirmed(MotionEvent event) {

                    // Trying to get bubble position but can't just get x=0, y=0 tried
                    // many things
                    Log.d(TAG,""+((ViewGroup)mFrame).getChildCount());


for(int i=0; i<((ViewGroup)mFrame).getChildCount(); ++i) {
                            View nextChild = ((ViewGroup)mFrame).getChildAt(i);
                            Rect rect = new Rect();
                            nextChild.getLocalVisibleRect(rect);
                            int[] location = new int[2];

                            nextChild.getLocationOnScreen(location);
                            Log.d(TAG, "X = " + location[0] + " Y = " + location[1]);
                        }
                    if(event.getAction() == MotionEvent.ACTION_DOWN){
                        BubbleView bubbleView = new BubbleView(getApplicationContext(), event.getX(),event.getY());
                        bubbleView.start();
                        mFrame.addView(bubbleView);
                    }

                    return true;
                }
            });
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    // TODO - delegate the touch to the gestureDetector


    return mGestureDetector.onTouchEvent(event);

}

@Override
protected void onPause() {

    // TODO - Release all SoundPool resources

    super.onPause();
}

// BubbleView is a View that displays a bubble.
// This class handles animating, drawing, popping amongst other actions.
// A new BubbleView is created for each bubble on the display

private class BubbleView extends View {

    private static final int BITMAP_SIZE = 64;
    private static final int REFRESH_RATE = 40;
    private final Paint mPainter = new Paint();
    private ScheduledFuture<?> mMoverFuture;
    private int mScaledBitmapWidth;
    private Bitmap mScaledBitmap;

    // location, speed and direction of the bubble
    private float mXPos, mYPos, mDx, mDy;
    private long mRotate, mDRotate;

    public BubbleView(Context context, float x, float y) {
        super(context);
        log("Creating Bubble at: x:" + x + " y:" + y);

        // Create a new random number generator to
        // randomize size, rotation, speed and direction
        Random r = new Random();

        // Creates the bubble bitmap for this BubbleView
        createScaledBitmap(r);

        // Adjust position to center the bubble under user's finger
        mXPos = x - mScaledBitmapWidth / 2;
        mYPos = y - mScaledBitmapWidth / 2;

        // Set the BubbleView's speed and direction
        setSpeedAndDirection(r);

        // Set the BubbleView's rotation
        setRotation(r);

        mPainter.setAntiAlias(true);

    }


    // Start moving the BubbleView & updating the display
    private void start() {

        // Creates a WorkerThread
        ScheduledExecutorService executor = Executors
                .newScheduledThreadPool(1);

        // Execute the run() in Worker Thread every REFRESH_RATE
        // milliseconds
        // Save reference to this job in mMoverFuture
        mMoverFuture = executor.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                // TODO - implement movement logic.
                // Each time this method is run the BubbleView should
                // move one step. If the BubbleView exits the display,
                // stop the BubbleView's Worker Thread.
                // Otherwise, request that the BubbleView be redrawn.
                if(!isOutOfView()){
                    moveWhileOnScreen();
                }
                else{
                    stop(true);
                }

            }
        }, 0, REFRESH_RATE, TimeUnit.MILLISECONDS);
    }

    private synchronized boolean intersects(float x, float y) {

        // TODO - Return true if the BubbleView intersects position (x,y)

        return false;
    }

    // Cancel the Bubble's movement
    // Remove Bubble from mFrame
    // Play pop sound if the BubbleView was popped

    private void stop(final boolean popped) {

        if (null != mMoverFuture && mMoverFuture.cancel(true)) {

            // This work will be performed on the UI Thread

            mFrame.post(new Runnable() {
                @Override
                public void run() {

                    // TODO - Remove the BubbleView from mFrame

                    if (popped) {
                        log("Pop!");

                        // TODO - If the bubble was popped by user,
                        // play the popping sound
                        mFrame.removeView(BubbleView.this);
                        //mMoverFuture.cancel(true);
                        mSoundPool.play(mSoundID, 1, 1, 1, 0, 1);
                    }

                    log("Bubble removed from view!");

                }
            });
        }
    }

    // Change the Bubble's speed and direction
    private synchronized void deflect(float velocityX, float velocityY) {
        log("velocity X:" + velocityX + " velocity Y:" + velocityY);

        //TODO - set mDx and mDy to be the new velocities divided by the REFRESH_RATE

        mDx = velocityX/REFRESH_RATE;
        mDy = velocityY/REFRESH_RATE;

    }

    // Draw the Bubble at its current location
    @Override
    protected synchronized void onDraw(Canvas canvas) {

        // TODO - save the canvas
        canvas.save();

        // TODO - increase the rotation of the original image by mDRotate
        mRotate = mRotate + mDRotate;

        // TODO Rotate the canvas by current rotation
        canvas.rotate(mRotate, mXPos + mScaledBitmapWidth/2, mYPos + mScaledBitmapWidth/2);

        // TODO - draw the bitmap at it's new location
        canvas.drawBitmap(mScaledBitmap, mXPos, mYPos,mPainter);


        // TODO - restore the canvas
        canvas.restore();



    }


    private synchronized boolean moveWhileOnScreen() {

        // TODO - Move the BubbleView
        // Returns true if the BubbleView has exited the screen
        mXPos = mDx+mXPos;
        mYPos = mDy+mYPos;

        postInvalidate();
        return false;
    }

    private boolean isOutOfView() {

        // TODO - Return true if the BubbleView has exited the screen
        if(mXPos + mScaledBitmapWidth/2 >= mDisplayWidth - mScaledBitmapWidth/2 || mXPos  <0
                ||mYPos + mScaledBitmapWidth/2 >= mDisplayHeight - mScaledBitmapWidth/2 || mYPos  <0){
            return true;
        }
        return false;

    }
}

Update :-

To clarify a bit, I want to get the location of all the bubbles on the screen and then compare them to event.getX() and event.getY() to detect if i tapped on any bubble. II have to check bubble tap in onSingleTapConfirmed(). I am correctly able to get the total number of bubbles but can't detect their location on the screen.

for(int i=0; i<((ViewGroup)mFrame).getChildCount(); ++i) {
                            View nextChild = ((ViewGroup)mFrame).getChildAt(i);
                            Rect rect = new Rect();
                            nextChild.getLocalVisibleRect(rect);
                            int[] location = new int[2];

                            nextChild.getLocationOnScreen(location);
                            Log.d(TAG, "X = " + location[0] + " Y = " + location[1]);
                        }

Above code gives the correct number of bubbles but return their coordinates as 0,0.

Was it helpful?

Solution

In your onSingleTapConfirmed function, try the following to iterate through your BubbleViews and pass the Event X and Y coordinates on.

for(int i=0;i<mFrame.getChildCount();i++){
    BubbleView bubbleThis = (BubbleView) mFrame.getChildAt(i);
    if (bubbleThis.intersects(event.getX(),event.getY())){
        bubbleThis.stop(true);
        return true;
    }
}

The function in BubbleView should then return true if the X and Y fall inside its boundaries. I will add the function inside intersects function in BubbleView as clarification:

private synchronized boolean intersects(float x, float y) {
    if ( (x>mXPos && x<(mXPos+mScaledBitmapWidth)) && (y>mYPos && y<(mYPos+mScaledBitmapWidth)) ) {
        return true;
    }
    return false;
}

OTHER TIPS

If you want to know if a user tapped a bubble, set its onClickListener. If you want to know if the user just touched it, override its onTouchEvent and look for ACTION_DOWN.

How are you implementing the onDown() method of your SimpleOnGestureListener?

Please take a look at these answers: Gesture Detector not working

Android GestureDetector with SimpleOnGestureListener within SurfaceView

Detect which View was tapped in the onSingleTapConfirmed method

Bubble is circle in shape, so you just need to compare its radius with the distance between bubble center and the position.

mRadius = radius of the bubble

mDistance = distance between (event.getX(), event.getY()) and bubble center (mXPos + mRadius, mYPos + mRadius)

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