how to use both Ontouch and Onclick for an ImageButton?

Android

Android Problem Overview


in my app, i want two things to happen.

  1. when i touch and drag the ImageButton, it should move along with my finger.

    i used OnTouchListener() for this and it works fine.

  2. when i click the ImageButton, it should close the activity.

    i used OnClickListener() for this and it also works fine.

So, here is my problem. whenever i move the ImageButton OnTouchListener is tirggered and the ImageButton moves, the OnClickListener is also triggered at the end when i am releasing the button from moving.

How to use ontouch and onclick listeners on the same button without interfering on each other?

Android Solutions


Solution 1 - Android

Try this, It may help you

No need to set onClick() method onTouch() will handle both the case.

package com.example.demo;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageButton;

public class MainActivity extends Activity {
	private GestureDetector gestureDetector;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		gestureDetector = new GestureDetector(this, new SingleTapConfirm());
		ImageButton imageButton = (ImageButton) findViewById(R.id.img);

		imageButton.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {

				if (gestureDetector.onTouchEvent(arg1)) {
					// single tap
					return true;
				} else {
					// your code for move and drag
				}

				return false;
			}
		});

	}

	private class SingleTapConfirm extends SimpleOnGestureListener {

		@Override
		public boolean onSingleTapUp(MotionEvent event) {
			return true;
		}
	}

}

Solution 2 - Android

To have Click Listener, DoubleClick Listener, OnLongPress Listener, Swipe Left, Swipe Right, Swipe Up, Swipe Down on Single View you need to setOnTouchListener. i.e,

view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {
            
            @Override
            public void onClick() {
                super.onClick();
                // your on click here
            }

            @Override
            public void onDoubleClick() {
                super.onDoubleClick();
                // your on onDoubleClick here
            }

            @Override
            public void onLongClick() {
                super.onLongClick();
                // your on onLongClick here
            }

            @Override
            public void onSwipeUp() {
                super.onSwipeUp();
                // your swipe up here
            }

            @Override
            public void onSwipeDown() {
                super.onSwipeDown();
                // your swipe down here.
            }

            @Override
            public void onSwipeLeft() {
                super.onSwipeLeft();
                // your swipe left here.
            }

            @Override
            public void onSwipeRight() {
                super.onSwipeRight();
                // your swipe right here.
            }
        });

}

For this you need OnSwipeTouchListener class that implements OnTouchListener.

public class OnSwipeTouchListener implements View.OnTouchListener {

private GestureDetector gestureDetector;

public OnSwipeTouchListener(Context c) {
    gestureDetector = new GestureDetector(c, new GestureListener());
}

public boolean onTouch(final View view, final MotionEvent motionEvent) {
    return gestureDetector.onTouchEvent(motionEvent);
}

private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        onClick();
        return super.onSingleTapUp(e);
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        onDoubleClick();
        return super.onDoubleTap(e);
    }

    @Override
    public void onLongPress(MotionEvent e) {
        onLongClick();
        super.onLongPress(e);
    }

    // Determines the fling velocity and then fires the appropriate swipe event accordingly
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffX > 0) {
                        onSwipeRight();
                    } else {
                        onSwipeLeft();
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeDown();
                    } else {
                        onSwipeUp();
                    }
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

public void onSwipeRight() {
}

public void onSwipeLeft() {
}

public void onSwipeUp() {
}

public void onSwipeDown() {
}

public void onClick() {

}

public void onDoubleClick() {

}

public void onLongClick() {

}
}

Solution 3 - Android

The problem with onClick and OnTouch event is that the moment you Click(with the intention to Click) it assumes the event to be OnTouch thus OnClick is never interpreted. The work around

isMove = false;
case MotionEvent.ACTION_DOWN:
//Your stuff
isMove = false;
case MotionEvent.ACTION_UP:
if (!isMove || (Xdiff < 10 && Ydiff < 10 ) {
view.performClick; //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little
even when you just click it   
}
case MotionEvent.ACTION_MOVE:
isMove = true;

Solution 4 - Android

I've tried to apply @Biraj solution in my project and it didn't work - I've noticed that extension of SimpleOnGestureListener should not only override onSingleTapConfirmed method, but onDown as well. Due to documentation:

> If you return false from onDown(), as GestureDetector.SimpleOnGestureListener does by default, the system assumes that you want to ignore the rest of the gesture, and the other methods of GestureDetector.OnGestureListener never get called

Below is complex solution:

public class MainActivity extends Activity {
	private GestureDetector gestureDetector;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		gestureDetector = new GestureDetectorCompat(this, new SingleTapConfirm());
		ImageButton imageButton = (ImageButton) findViewById(R.id.img);

		imageButton.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View arg0, MotionEvent arg1) {

				if (gestureDetector.onTouchEvent(arg1)) {
					// single tap
					return true;
				} else {
					// your code for move and drag
				}

				return false;
			}
		});

	}

	private class SingleTapConfirm extends SimpleOnGestureListener {

		@Override
        public boolean onDown(MotionEvent e) {
            /*it needs to return true if we don't want 
            to ignore rest of the gestures*/
            return true;
        }

		@Override
		public boolean onSingleTapConfirmed(MotionEvent event) {
			return true;
		}
	}

}

I suppose, that this behaviour can be caused by difference GestureDetectorCompat, but I'll follow documentation and I'm going to use second one:

> You should use Support Library classes where possible to provide compatibility with devices running Android 1.6 and higher

Solution 5 - Android

In MainActivity code this.

public class OnSwipeTouchListener_imp extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_on_swipe_touch_listener);

    ImageView view = (ImageView)findViewById(R.id.view);

    view.setOnTouchListener(new OnSwipeTouchListener(OnSwipeTouchListener_imp.this)
    {
        @Override
        public void onClick()
        {
            super.onClick(); // your on click here              
            Toast.makeText(getApplicationContext(),"onClick",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDoubleClick()
        {
            super.onDoubleClick(); // your on onDoubleClick here               
        }

        @Override
        public void onLongClick()
        {
            super.onLongClick(); // your on onLongClick here                
        }

        @Override
        public void onSwipeUp() {
            super.onSwipeUp(); // your swipe up here                
        }

        @Override
        public void onSwipeDown() {
            super.onSwipeDown();  // your swipe down here.
          
        }

        @Override
        public void onSwipeLeft() {
            super.onSwipeLeft(); // your swipe left here.                
            Toast.makeText(getApplicationContext(),"onSwipeLeft",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onSwipeRight() {
            super.onSwipeRight(); // your swipe right here.                
            Toast.makeText(getApplicationContext(),"onSwipeRight",Toast.LENGTH_SHORT).show();
        }
    });
}
}

Then create a OnSwipeTouchListener java Class.

public class OnSwipeTouchListener implements View.OnTouchListener {

private GestureDetector gestureDetector;

public OnSwipeTouchListener(Context c) {
    gestureDetector = new GestureDetector(c, new GestureListener());
}

public boolean onTouch(final View view, final MotionEvent motionEvent) {
    return gestureDetector.onTouchEvent(motionEvent);
}

private final class GestureListener extends GestureDetector.SimpleOnGestureListener {

    private static final int SWIPE_THRESHOLD = 100;
    private static final int SWIPE_VELOCITY_THRESHOLD = 100;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        onClick();
        return super.onSingleTapUp(e);
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        onDoubleClick();
        return super.onDoubleTap(e);
    }

    @Override
    public void onLongPress(MotionEvent e) {
        onLongClick();
        super.onLongPress(e);
    }

    // Determines the fling velocity and then fires the appropriate swipe event accordingly
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        boolean result = false;
        try {
            float diffY = e2.getY() - e1.getY();
            float diffX = e2.getX() - e1.getX();
            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD)
                {
                    if (diffX > 0)
                    {
                        onSwipeRight(); // Right swipe
                    } else {
                        onSwipeLeft();  // Left swipe
                    }
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
                    if (diffY > 0) {
                        onSwipeDown(); // Down swipe
                    } else {
                        onSwipeUp(); // Up swipe
                    }
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return result;
    }
}

public void onSwipeRight() {
}

public void onSwipeLeft() {
}

public void onSwipeUp() {
}

public void onSwipeDown() {
}

public void onClick() {
}

public void onDoubleClick() {
}

public void onLongClick() {
}
}

Hope this lights u:)

Solution 6 - Android

Maybe you can work with a boolean.

lets say Boolean isMoving = false;

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

	case MotionEvent.ACTION_MOVE:
                 isMoving = true;

          // implement your move codes
    break;

    case MotionEvent.ACTION_UP:
               isMoving = false;
    break;

default:
		break;
	}

and then in your onclick method check for the boolean. If it is false, then perform the click action, if true ... do not act.

public void onClick(View arg0) {
	
	switch (arg0.getId()) {

    case R.id.imagebutton:
		if(!isMoving) {
                   //code for on click here
                }
	default:
		break;
	}
}

Solution 7 - Android

Hope I'm not too late but you can achieve this by time counter. A click takes less than a second so with that in mind....

    long prev=0;
    long current = 0;
    long dif =0; 
   public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    
                    prev = System.currentTimeMillis() / 1000;
                    
                    break;
                case MotionEvent.ACTION_UP:

                    current = System.currentTimeMillis() / 1000;
                    dif = current - prev;
                    if (dif == 0) {
                        //Perform Your Click Action
                    }
                    break;
                 }
           }

Hope it helps out someone

Solution 8 - Android

You can differentiate between touch, click and swipe by getting the difference in the x,y values like:

var prevY = 0
var prevX = 0
    controls.setOnTouchListener { v, event ->
    
                when (event?.actionMasked) {
    
                    MotionEvent.ACTION_DOWN -> {
                        prevY = event.rawY.toInt()
                        prevX = event.rawX.toInt()
                        
                    }
 
                    MotionEvent.ACTION_UP->{
                        Log.d("Controls","Action up")
                        var y = event.rawY.toInt()
                        var x = event.rawX.toInt()
                        val diffY = Math.abs(prevY - y)
                        val diffX = Math.abs(prevX - x)
                        if(diffX in 0..10 && diffY in 0..10){
                          // its a touch
                        }
                         //check diffY if negative, is a swipe down, else swipe up
                      //check diffX if negative, its a swipe right, else swipe left
                    }
                }
                true
            }

Solution 9 - Android

Clieck Listener event; if actiondown and action up in your components bounds than call onclicklistener. Thus onclick event activate with touch detection. You could only set onTouchListener and receive click events if action up and action down in your components start position.

Solution 10 - Android

Declare a private variable like: boolean hasMoved = false;

When the image button begins to move, set hasMoved = true

In your OnClickListener execute code only if(!hasMoved) -- meaning perform on click functionality only if the button has not moved. Set hasMoved = false; afterwards

Solution 11 - Android

Simply use a boolean field and set it to true value when your OnTouchListener is triggered. after that when the OnClickListener wants to trigger you will check the boolean field and if true don't act anything in your onClickListener.

    private blnTouch = false;

private OnTouchListener btnOnTouchListener = new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
	// TODO Auto-generated method stub
	if (event.getAction()==MotionEvent.ACTION_DOWN){
		blnOnTouch = true;
	}
	if (event.getAction()==MotionEvent.ACTION_UP){
		blnOnTouch = false;
	}
               }
 };

private OnClickListener btnOnClickListener = new OnClickListener() {
	
	@Override
	public void onClick(View arg0) {
		if (blnOnTouch){
			// Do Your OnClickJob Here without touch and move
		}
		
	}
};

Solution 12 - Android

Perfect example for Both touch n tap both    
private void touchNtap() {
            GestureDetector gestureDetector = new GestureDetector(itemView.getContext()
                    , new SingleTapConfirm());
    
    
            imageView.setOnTouchListener(new View.OnTouchListener() {
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (gestureDetector.onTouchEvent(event)) {
                        // program for click events
                        gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
                            @Override
                            public boolean onSingleTapConfirmed(MotionEvent e) {
                                Log.d(TAG, "onSingleTapConfirmed() returned: " + true);
                                return false;
                            }
    
                            @Override
                            public boolean onDoubleTap(MotionEvent e) {
                                Log.d(TAG, "onDoubleTap() returned: " + true);
                                return false;
                            }
    
                            @Override
                            public boolean onDoubleTapEvent(MotionEvent e) {
                                Log.d(TAG, "onDoubleTapEvent() returned: " + true);
                                return false;
                            }
                        });
                        return true;
                    }else {
                        //program for touch events
                    }
                    return false;
                }
            });
        }
Create a Inner class 

    private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
    
            @Override
            public boolean onSingleTapUp(MotionEvent event) {
                return true;
            }
    
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                return true;
            }
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPraveen ThamizhazhaganView Question on Stackoverflow
Solution 1 - AndroidBiraj ZalavadiaView Answer on Stackoverflow
Solution 2 - AndroidJaydipsinh ZalaView Answer on Stackoverflow
Solution 3 - AndroidDeepika AnandView Answer on Stackoverflow
Solution 4 - AndroidKrzysztof SkrzyneckiView Answer on Stackoverflow
Solution 5 - AndroidAgilanbuView Answer on Stackoverflow
Solution 6 - AndroidrmullerView Answer on Stackoverflow
Solution 7 - AndroidDr AdamsView Answer on Stackoverflow
Solution 8 - AndroidozmankView Answer on Stackoverflow
Solution 9 - AndroidnurisezginView Answer on Stackoverflow
Solution 10 - AndroidAmulya KhareView Answer on Stackoverflow
Solution 11 - AndroidMajid DaeinejadView Answer on Stackoverflow
Solution 12 - AndroidVikash SharmaView Answer on Stackoverflow