11

My class extends View and I need to get continuous touch events on it.

If I use:

public boolean onTouchEvent(MotionEvent me) {

    if(me.getAction()==MotionEvent.ACTION_DOWN) {
        myAction();
    }
    return true;
}

... the touch event is captured once.

What if I need to get continuous touches without moving the finger? Please, tell me I don't need to use threads or timers. My app is already too much heavy.

Thanks.

daliz
  • 840
  • 2
  • 12
  • 25

8 Answers8

16

Use if(me.getAction() == MotionEvent.ACTION_MOVE). It's impossible to keep a finger 100% completely still on the screen so Action_Move will get called every time the finger moves, even if it's only a pixel or two.

You could also listen for me.getAction() == MotionEvent.ACTION_UP - until that happens, the user must still have their finger on the screen.

Steve Haley
  • 55,374
  • 17
  • 77
  • 85
  • 2
    Correct, I would prefer the solution to set a flag on ACTION_DOWN and use that until the flag is set to false on ACTION_UP – WarrenFaith Mar 29 '10 at 17:06
  • I see this mentioned everywhere but it doesn't seem to be the case for me. If I touch and hold and stay still, I do not get ACTION_MOVE events. ACTION_MOVE only seem to trigger if I lean my finger one way or the other. Could it be a setting on my phone that is keeping it less sensitive? – clusterflux May 18 '13 at 08:31
  • 1
    @clusterflux if you're relying on ACTION_MOVE to trigger continuously, even if the finger is being held still, you're probably doing something wrong. The second half of my (very old!) answer, e.g. set a flag in down and clear it in up, is really the right way to do this. – Steve Haley May 22 '13 at 14:06
  • 1
    @SteveHaley Thanks for the help. I have a follow up question. I have a function that is called on ACTION_DOWN. I want it to be called continuously until ACTION_UP occurs. I have tried to use a flag as you suggested, but the problem I am having is that I'm using a while(!up_flag) loop to repeatedly call the function once an ACTION_DOWN occurs. However, once I'm in the while() loop I cannot sense anymore touch events, so ACTION_UP is never sensed, so the flag is not changed. I get stuck in a continuous while loop. Any suggestions on an alternative method? – clusterflux Jul 02 '13 at 10:19
  • Good but... Not working on an emulator. Unlike fingers, the mouse could stay still. – Meow Cat 2012 Mar 07 '19 at 02:21
5

You need to set this properties for the element android:focusable="true" android:clickable="true" if not, just produce the down action.

Nicolás Loaiza
  • 1,015
  • 11
  • 11
4

Her is the simple code snippet which shows that how you can handle the continues touch event. When you touch the device and hold the touch and move your finder, the Touch Move action performed.

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    if(isTsunami){
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // Write your code to perform an action on down
            break;
        case MotionEvent.ACTION_MOVE:
            // Write your code to perform an action on contineus touch move
            break;
        case MotionEvent.ACTION_UP:
            // Write your code to perform an action on touch up
            break;
    }
    }
    return true;
}
Rahul Patel
  • 1,198
  • 7
  • 5
1

This might help,

requestDisallowInterceptTouchEvent(true);

on the parent view, like this -

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            view.getParent().requestDisallowInterceptTouchEvent(true);
            switch(motionEvent.getAction()){
            }

            return false; 

         }
Akshay Sahai
  • 2,121
  • 1
  • 17
  • 20
1

Try this. It works to me:

public static OnTouchListener loadContainerOnTouchListener() {
    OnTouchListener listener = new OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        LinearLayout layout = (LinearLayout)v;
        for(int i =0; i< layout.getChildCount(); i++)
        {
            View view = layout.getChildAt(i);
            Rect outRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
            if(outRect.contains((int)event.getX(), (int)event.getY()))
            {
                Log.d(this.getClass().getName(), String.format("Over view.id[%d]", view.getId()));
            }
        }

    }

Remember: the listener you´ll set must be a container layout (Grid, Relative, Linear).

LinearLayout layout = findViewById(R.id.yourlayoutid);
layout.setOnTouchListener(HelperClass.loadContainerOnTouchListener());
Andro Selva
  • 53,910
  • 52
  • 193
  • 240
Garry
  • 161
  • 1
  • 3
0

I was making a game with a custom view used as a thumb control. . . here is what I did

float x = 0, y = 0;

@Override
public boolean onTouchEvent(MotionEvent event) {
    x = event.getX();
    y = event.getY();

    // handle touch events with 
    switch( event.getActionMasked() ) {
        case MotionEvent.ACTION_DOWN :
            if(cont)
            {
                // remove any previous callbacks
                removeCallbacks(contin);

                // post new runnable
                postDelayed(contin, 10);
            }
            invalidate();
            return true;
        case MotionEvent.ACTION_MOVE :
            if(!cont && thumbing != null)
            {
                //  do non-continuous operations here
            }
            invalidate();       
            return true;
        case MotionEvent.ACTION_UP :

            // set runnable condition to false
            x = 0;

            // remove the callbacks to the thread
            removeCallbacks(contin);
            invalidate();
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

public boolean cont = false;

// sets input to continuous
public void set_continuous(boolean b) { cont = b; }


public Runnable contin = new Runnable()
{

    @Override
    public void run() {
        if(x != 0)
        {
            //  do continuous operations here
            postDelayed(this, 10);
        }
    }

};

A quick note however, make sure in your main activity that is calling this view removes the callbacks manually via the onPause method as follows

@Override
protected void onPause() {
    if(left.cont) left.removeCallbacks(left.contin);
    if(right.cont) right.removeCallbacks(left.contin); 
    super.onPause();
}

That way if you pause and come back touch events aren't being handled twice and the view is free from it's thread's overhead.

** tested on Samsung Galaxy S3 with hardware acceleration on **

Chris Sullivan
  • 576
  • 5
  • 10
0

All these answer are partially correct but they do not resolve in the right way the problem.

First of all, for everyone out there that decide to track when the event is ACTION_MOVE. Well that works only guess when? When user move his finger, so could if you decide to implement a custom thumb control is okay but for a normal custom button that's not the case.

Second, using a flag inside ACTION_DOWN and check it in ACTION_UP seems the logic way to do it, but as Clusterfux find out if you implement a while(!up_flag) logic you get stuck into troubles ;)

So the proper way to do it is mentioned here:

Continuous "Action_DOWN" in Android

Just keep in mind that if the logic you're going to write during the continuous press has to modify the UI in some way, you have to do it from the main thread in all the other cases it's better use another thread.

Paolo Moschini
  • 352
  • 3
  • 3
0

You can use the below code snippet as a reference in which I used the background to detect if the screen is held or not...

Main_Layout.setOnTouchListener(new View.OnTouchListener() {
        @SuppressLint("ResourceAsColor")
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    Main_Layout.setBackgroundColor(R.color.green);
                    event.setAction(MotionEvent.ACTION_DOWN);
                    break;
                default:
                    Main_Layout.setBackgroundColor(R.color.blue);
                    break;

            }
            return false;
        }
    });
Aaraf Rao
  • 19
  • 1
  • 4