Reputation: 841
in my app, i want two things to happen.
when i touch and drag the ImageButton, it should move along with my finger.
i used OnTouchListener()
for this and it works fine.
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?
Upvotes: 69
Views: 77087
Reputation: 539
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;
}
}
Upvotes: 0
Reputation: 763
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
}
Upvotes: 1
Reputation: 276
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
Upvotes: 1
Reputation: 2805
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:)
Upvotes: 6
Reputation: 28484
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;
}
}
}
Upvotes: 141
Reputation: 2525
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
Upvotes: 6
Reputation: 16798
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() {
}
}
Upvotes: 36
Reputation: 305
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;
Upvotes: 9
Reputation: 1837
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;
}
}
Upvotes: 2
Reputation: 1037
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
}
}
};
Upvotes: 0
Reputation: 7698
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
Upvotes: 0
Reputation: 1570
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.
Upvotes: 0