[英]Can't handle both click and touch events simultaneously
我正在尝试处理按钮上的触摸事件和单击事件。 我执行以下操作:
button.setOnClickListener(clickListener);
button.setOnTouchListener(touchListener);
当任何一个监听器被注册时,一切正常,但是当我尝试同时使用它们时,只会触发触摸事件。 任何解决方法? 我究竟做错了什么?
它有点棘手。
如果您设置onTouchListener
,则需要在ACTION_DOWN
返回true
,以告诉系统我已经消耗了该事件并且它不会滴入其他侦听器。
但是OnClickListener
不会被触发。
所以你可能会想,我会在那里做我的事情并返回false
以便我也可以接收点击。 如果你这样做,它会工作,但你不会订阅其他即将到来的触摸事件( ACTION_MOVE
, ACTION_UP
)因此,唯一的选择是在那里返回true
,但是你不会收到我们所说的任何点击事件之前。
所以你需要在ACTION_UP
使用view.performClick()
手动执行点击
这将起作用。
ClickListener
和TouchListener
之间存在细微但非常重要的区别。 在视图可以响应事件之前执行TouchListener
。 只有在视图处理它之后, ClickListener
才会接收它的事件。
因此,当您触摸屏幕时, TouchListener
执行TouchListener
,当您为事件返回true
时, ClickListener
将永远不会得到它。 但是,如果你按你的设备的轨迹球,在ClickListener
应该被解雇,因为TouchListener
不会对此作出回应。
感谢@urSus 的精彩回答
但在这种情况下,每次触摸都会执行点击,即使是 ACTION_MOVE
假设你想分开move
事件和click
事件,你可以使用一个小技巧
定义一个boolean
字段并像这样使用:
@Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
shouldClick = true;
.
.
break;
case MotionEvent.ACTION_UP:
if (shouldClick)
view.performClick();
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
//Do your stuff
shouldClick = false;
break;
}
rootLayout.invalidate();
return true;
}
您应该在OnTouchListener
返回 false,然后您的OnClickListener
也将被处理。
我想你在你的OnTouchListener
返回true
? 这将消耗该事件,因此它不会被发送以进行任何进一步处理。
附带说明 - 同时拥有点击和触摸侦听器有什么意义?
以上所有答案都说我们不能同时处理setOnTouchListener
和setOnClickListener
。
但是,我看到我们可以通过在setOnTouchListener
return false
来处理两者
例子
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
}
}
当我单击Button
,logcat 将显示为
I/TAG: onTouch 0
I/TAG: onTouch 1
I/TAG: onClick
这是一个不影响 ClickListener 的 TouchListener 示例。
import android.graphics.PointF
import android.view.MotionEvent
import android.view.MotionEvent.*
import android.view.View
import kotlin.math.abs
object TouchAndClickListener : View.OnTouchListener {
/** Those are factors you can change as you prefer */
private const val touchMoveFactor = 10
private const val touchTimeFactor = 200
private var actionDownPoint = PointF(0f, 0f)
private var previousPoint = PointF(0f, 0f)
private var touchDownTime = 0L
override fun onTouch(v: View, event: MotionEvent) = when (event.action) {
ACTION_DOWN -> PointF(event.x, event.y).let {
actionDownPoint = it // Store it to compare position when ACTION_UP
previousPoint = it // Store it to compare position when ACTION_MOVE
touchDownTime = now() // Store it to compare time when ACTION_UP
/* Do other stuff related to ACTION_DOWN you may whant here */
true
}
ACTION_UP -> PointF(event.x, event.y).let {
val isTouchDuration = now() - touchDownTime < touchTimeFactor // short time should mean this is a click
val isTouchLength = abs(it.x - actionDownPoint.x) + abs(it.y - actionDownPoint.y) < touchMoveFactor // short length should mean this is a click
val shouldClick = isTouchLength && isTouchDuration // Check both
if (shouldClick) yourView.performClick() //Previously define setOnClickListener{ } on yourView, then performClick() will call it
/* Do other stuff related to ACTION_UP you may whant here */
true
}
ACTION_MOVE -> PointF(event.x, event.y).let {
/* Do other stuff related to ACTION_MOVE you may whant here */
previousPoint = it
true
}
else -> false // Nothing particular with other event
}
private fun now() = System.currentTimeMillis()
}
感谢@Nicolas Duponchel 这就是我实现 onClick 和 onTouch 事件的方式
`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();
break;
}
}
}`
button.setOnTouchListener(this);
实现接口和代码在这里:
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (view.getId()) {
case R.id.send:
switch(motionEvent.getAction()){
case MotionEvent.ACTION_DOWN:
//when the user has pressed the button
//do the needful here
break;
case MotionEvent.ACTION_UP:
//when the user releases the button
//do the needful here
break;
}
break;
}
return false;
}
为了使 gridview 中的这两个事件都成为可能,只需按如下方式返回触摸侦听器“false”,这对我有用。
**GridView myView = findViewById(R.id.grid_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// ... Respond to touch events
return false;
}
});**
这样两个事件都可以实现
## 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;
}
在ACTION_UP
使用条件手动执行onClick
boolean shouldClick = event.eventTime - event.downTime <= 200 // compares the time with some threshold
所以,在MotionEvent.ACTION_UP
尝试
if(event.eventTime - event.downTime <= 200) { // case or when statement of action Touch listener
view.performClick();
}
我知道为时已晚,但是如果有人正在为此寻找干净的解决方案而苦苦挣扎,那么就在这里。
这些用于测量触摸和移开手指之间的时间。
private long clickTime = 0;
public static final long CLICK_TIMEOUT = 200; // 200ms
这是我的onTouchListner
。 奇迹般有效
private final View.OnTouchListener onTouchListener = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
clickTime = System.currentTimeMillis();
return true;
} else if(event.getAction() == MotionEvent.ACTION_UP) {
if(System.currentTimeMillis()-clickTime < Constants.CLICK_TIMEOUT)
{
Toast.makeText(getContext(), "clicked", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE){
if(System.currentTimeMillis()-clickTime > Constants.CLICK_TIMEOUT)
{
ClipData data = ClipData.newPlainText("" , "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
v.startDrag(data , shadowBuilder , v , 0);
return false;
}
return false;
}
return false;
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.