Question

In my Android app, I have a custom View that receives touch events. However, it doesn't react every time I touch it - only sometimes. From what I can tell, if I touch the screen, move my finger, and then let go - even if I move only a little - the event is picked up, but if I tap the screen too quickly for my finger to slide across it, nothing happens. How can I fix this?

Here is the View's code:

public class SpeedShooterGameView extends GameActivity.GameView {
    public SpeedShooterGameView(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
    }

    @Override
    protected GameThread getNewThread(SurfaceHolder holder, Context context) {
        return new SpeedShooterGameThread(holder, context);
    }

    // Program is driven by screen touches
    public boolean onTouchEvent(MotionEvent event) {
        SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread();
        if (thread.isRunning()) {
            return thread.recieveTouch(event);
        } else {
             return false;
        }
    }
}

I am pretty confident that the object returned in the line SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread(); is working as I expect it to, but if the code above looks fine, I'll post the relevant code from that class as well. When thread.recieveTouch(event); is called, the MotionEvent is being sent to another thread.

EDIT: I'll go ahead and post the code for SpeedShooterGameThread:

public class SpeedShooterGameThread extends GameActivity.GameView.GameThread {
    //... snip ...
    private Queue<MotionEvent> touchEventQueue;

    //... snip ...

    public synchronized final void newGame() { //called from the constructor, used to go to a known stable state
        //... snip ...
        touchEventQueue = new LinkedList<MotionEvent>();
        //... snip ...
    }

    //...snip...

    public synchronized boolean recieveTouch(MotionEvent event) {
        return touchEventQueue.offer(event);
    }

    private synchronized void processTouchEvents() {
        synchronized (touchEventQueue) {
            while (!touchEventQueue.isEmpty()) {
                MotionEvent event = touchEventQueue.poll();
                if (event == null) {
                    continue;
                }
                //... snip ....
            }
        }
    }

    //... snip ...
}
Was it helpful?

Solution

I fixed the bug by taking the Queue<MotionEvent> out entirely. My code now looks something like this:

The thread no longer uses a Queue, and MotionEvents are immediately processed when recieveTouch() is called:

public class SpeedShooterGameThread extends GameActivity.GameView.GameThread {
//The touchEvent member has been removed.

//... snip ...

    public synchronized final void newGame() { //called from the constructor, used to go to a known stable state
        // touchEvents is no longer initialized.

        //...snip...
    }

    //...snip...

    public synchronized boolean recieveTouch(MotionEvent event) {
        //Immediately handle the MotionEvent here,
        //or return false if the event isn't processed
    }

    // The processTouchEvents() method is removed.

    //... snip ...
}

The view is unchanged:

public class SpeedShooterGameView extends GameActivity.GameView {
    public SpeedShooterGameView(Context arg0, AttributeSet arg1) {
        super(arg0, arg1);
    }

    @Override
    protected GameThread getNewThread(SurfaceHolder holder, Context context) {
        return new SpeedShooterGameThread(holder, context);
    }

    // Program is driven by screen touches
    public boolean onTouchEvent(MotionEvent event) {
        SpeedShooterGameThread thread = (SpeedShooterGameThread) getThread();
        if (thread.isRunning()) {
            return thread.recieveTouch(event);
        } else {
             return false;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top