I am trying to handle touch events and click events on a button. I do the following:
button.setOnClickListener(clickListener);
button.setOnTouchListener(touc
You should return false in your OnTouchListener
then your OnClickListener
will be also handled.
thanks to @Nicolas Duponchel this is how i achieved both onClick and onTouch events
private short touchMoveFactor = 10;
private short touchTimeFactor = 200;
private PointF actionDownPoint = new PointF(0f, 0f);
private long touchDownTime = 0L;
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
Log.d(TAG, "onTouch: _____________ACTION_DOWN");
actionDownPoint.x = event.getX();
actionDownPoint.y = event.getY();
touchDownTime = System.currentTimeMillis();
break;
}
case MotionEvent.ACTION_UP: {
Log.d(TAG, "onTouch: _____________ACTION_UP");
//check if the user's finger is still close to the point he/she clicked
boolean isTouchLength = (Math.abs(event.getX() - actionDownPoint.x)
+ Math.abs(event.getY() - actionDownPoint.y)) < touchMoveFactor;
//check if it's not been more than @touchTimeFactor=200 ms
boolean isClickTime = System.currentTimeMillis() - touchDownTime < touchTimeFactor;
if (isTouchLength && isClickTime) performClick();
//ACTION_UP logic goes below this line
.....
.....
break;
}
case MotionEvent.ACTION_MOVE: {
//move method should not work if the click was too short i.e click time +100 ms
if (System.currentTimeMillis() - touchDownTime < touchTimeFactor+100) return true;
Log.d(TAG, "onTouch: _____________ACTION_MOVE");
// move method logic goes below this line
.....
.....
}}}
## Exact working solution for both click action and touch listener(dragging) ##
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
private float CLICK_ACTION_THRESHOLD = 0.5f;
private float startX;
private float startY;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (view.getId()) {
case R.id.chat_head_profile_iv:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
startX = event.getX();
startY = event.getY();
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
float endX = event.getX();
float endY = event.getY();
if (shouldClickActionWork(startX, endX, startY, endY)) {
openScreen();// WE HAVE A CLICK!!
}
return true;
case MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mChatHeadView, params);
return true;
}
break;
}
return true;
}
private boolean shouldClickActionWork(float startX, float endX, float startY, float endY) {
float differenceX = Math.abs(startX - endX);
float differenceY = Math.abs(startY - endY);
if ((CLICK_ACTION_THRESHOLD > differenceX) && (CLICK_ACTION_THRESHOLD > differenceY))
return true;
else
return false;
}
There is a subtle, yet very important difference between the ClickListener
and the TouchListener
. The TouchListener
is executed before the view can respond to the event. The ClickListener
will receive its event only after the view has handled it.
So when you touch your screen, the TouchListener
is executed first and when you return true
for your event, the ClickListener
will never get it. But if you press the trackball of your device, the ClickListener
should be fired because the TouchListener
will not respond to it.
Its a little tricky.
If you set onTouchListener
you need to return true
in ACTION_DOWN
, to tell the system that I have consumed the event and it won't trickle down to other listeners.
But then OnClickListener
won't be fired.
So you might think, I will just do my thing there and return false
so I can receive clicks too.
If you do so, it will work, but you won't be subscribed to other upcoming touch events (ACTION_MOVE
, ACTION_UP
)
Therefore, the only option is to return true
there, but then you won't receive any click events as we said previously.
So you need to perform the click manually in the ACTION_UP
with view.performClick()
This will work.
All above answer is said that we can not handle both setOnTouchListener
and setOnClickListener
.
However, I see we can handle both by return false
in setOnTouchListener
Example
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.button)
button.setOnClickListener {
Log.i("TAG", "onClick")
}
button.setOnTouchListener { v, event ->
Log.i("TAG", "onTouch " + event.action)
false
}
}
When I click at Button
, logcat will display like
I/TAG: onTouch 0
I/TAG: onTouch 1
I/TAG: onClick