我一直在尝试构建一个可以检测双击和三击的敲击检测器。在我的努力失败之后,我在网上搜索了很长时间以找到可以使用的东西,但没有运气!奇怪的是,像这样的东西的图书馆是如此稀缺。任何帮助?
答案 0 :(得分:34)
你可以尝试这样的事情。
虽然我一般建议不要使用三重点击作为模式,因为它不是用户通常习惯的东西,所以除非它正确地传达给他们,否则大多数人可能永远不会知道他们可以三重点击视图。实际上在移动设备上进行双重录制也是如此,在该环境中进行交互并不总是直观的方式。
view.setOnTouchListener(new View.OnTouchListener() {
Handler handler = new Handler();
int numberOfTaps = 0;
long lastTapTimeMs = 0;
long touchDownMs = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownMs = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
handler.removeCallbacksAndMessages(null);
if ((System.currentTimeMillis() - touchDownMs) > ViewConfiguration.getTapTimeout()) {
//it was not a tap
numberOfTaps = 0;
lastTapTimeMs = 0;
break;
}
if (numberOfTaps > 0
&& (System.currentTimeMillis() - lastTapTimeMs) < ViewConfiguration.getDoubleTapTimeout()) {
numberOfTaps += 1;
} else {
numberOfTaps = 1;
}
lastTapTimeMs = System.currentTimeMillis();
if (numberOfTaps == 3) {
Toast.makeText(getApplicationContext(), "triple", Toast.LENGTH_SHORT).show();
//handle triple tap
} else if (numberOfTaps == 2) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
//handle double tap
Toast.makeText(getApplicationContext(), "double", Toast.LENGTH_SHORT).show();
}
}, ViewConfiguration.getDoubleTapTimeout());
}
}
return true;
}
});
答案 1 :(得分:2)
我开发了适合我的需求的Iorgu solition的高级版本:
public abstract class OnTouchMultipleTapListener implements View.OnTouchListener {
Handler handler = new Handler();
private boolean manageInActionDown;
private float tapTimeoutMultiplier;
private int numberOfTaps = 0;
private long lastTapTimeMs = 0;
private long touchDownMs = 0;
public OnTouchMultipleTapListener() {
this(false, 1);
}
public OnTouchMultipleTapListener(boolean manageInActionDown, float tapTimeoutMultiplier) {
this.manageInActionDown = manageInActionDown;
this.tapTimeoutMultiplier = tapTimeoutMultiplier;
}
/**
*
* @param e
* @param numberOfTaps
*/
public abstract void onMultipleTapEvent(MotionEvent e, int numberOfTaps);
@Override
public final boolean onTouch(View v, final MotionEvent event) {
if (manageInActionDown) {
onTouchDownManagement(v, event);
} else {
onTouchUpManagement(v, event);
}
return true;
}
private void onTouchDownManagement(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownMs = System.currentTimeMillis();
handler.removeCallbacksAndMessages(null);
if (numberOfTaps > 0 && (System.currentTimeMillis() - lastTapTimeMs) < ViewConfiguration.getTapTimeout() * tapTimeoutMultiplier) {
numberOfTaps += 1;
} else {
numberOfTaps = 1;
}
lastTapTimeMs = System.currentTimeMillis();
if (numberOfTaps > 0) {
final MotionEvent finalMotionEvent = MotionEvent.obtain(event); // to avoid side effects
handler.postDelayed(new Runnable() {
@Override
public void run() {
onMultipleTapEvent(finalMotionEvent, numberOfTaps);
}
}, (long) (ViewConfiguration.getDoubleTapTimeout() * tapTimeoutMultiplier));
}
break;
case MotionEvent.ACTION_UP:
break;
}
}
private void onTouchUpManagement(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownMs = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
handler.removeCallbacksAndMessages(null);
if ((System.currentTimeMillis() - touchDownMs) > ViewConfiguration.getTapTimeout()) {
numberOfTaps = 0;
lastTapTimeMs = 0;
break;
}
if (numberOfTaps > 0 && (System.currentTimeMillis() - lastTapTimeMs) < ViewConfiguration.getDoubleTapTimeout()) {
numberOfTaps += 1;
} else {
numberOfTaps = 1;
}
lastTapTimeMs = System.currentTimeMillis();
if (numberOfTaps > 0) {
final MotionEvent finalMotionEvent = MotionEvent.obtain(event); // to avoid side effects
handler.postDelayed(new Runnable() {
@Override
public void run() {
onMultipleTapEvent(finalMotionEvent, numberOfTaps);
}
}, ViewConfiguration.getDoubleTapTimeout());
}
}
}
}
答案 2 :(得分:0)
使用视图侦听器检测第一次点击视图对象,然后查看如何管理两次后退以退出stackoverflow.com上的活动(使用处理程序后延迟)。
答案 3 :(得分:0)
这里是Kotlin的实现,可以检测任意数量的抽头,并遵守ViewConfiguration类中发现的各种超时和倾斜参数。我试图最小化事件处理程序中的堆分配。
import android.os.Handler
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import kotlin.math.abs
/*
* Detects an arbitrary number of taps in rapid succession
*
* The passed callback will be called for each tap, with two parameters:
* - the number of taps detected in rapid succession so far
* - a boolean flag indicating whether this is last tap of the sequence
*/
class MultiTapDetector(view: View, callback: (Int, Boolean) -> Unit) {
private var numberOfTaps = 0
private val handler = Handler()
private val doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout().toLong()
private val tapTimeout = ViewConfiguration.getTapTimeout().toLong()
private val longPressTimeout = ViewConfiguration.getLongPressTimeout().toLong()
private val viewConfig = ViewConfiguration.get(view.context)
private var downEvent = Event()
private var lastTapUpEvent = Event()
data class Event(var time: Long = 0, var x: Float = 0f, var y: Float = 0f) {
fun copyFrom(motionEvent: MotionEvent) {
time = motionEvent.eventTime
x = motionEvent.x
y = motionEvent.y
}
fun clear() {
time = 0
}
}
init {
view.setOnTouchListener { v, event ->
when(event.action) {
MotionEvent.ACTION_DOWN -> {
if(event.pointerCount == 1) {
downEvent.copyFrom(event)
} else {
downEvent.clear()
}
}
MotionEvent.ACTION_MOVE -> {
// If a move greater than the allowed slop happens before timeout, then this is a scroll and not a tap
if(event.eventTime - event.downTime < tapTimeout
&& abs(event.x - downEvent.x) > viewConfig.scaledTouchSlop
&& abs(event.y - downEvent.y) > viewConfig.scaledTouchSlop) {
downEvent.clear()
}
}
MotionEvent.ACTION_UP -> {
val downEvent = this.downEvent
val lastTapUpEvent = this.lastTapUpEvent
if(downEvent.time > 0 && event.eventTime - event.downTime < longPressTimeout) {
// We have a tap
if(lastTapUpEvent.time > 0
&& event.eventTime - lastTapUpEvent.time < doubleTapTimeout
&& abs(event.x - lastTapUpEvent.x) < viewConfig.scaledDoubleTapSlop
&& abs(event.y - lastTapUpEvent.y) < viewConfig.scaledDoubleTapSlop) {
// Double tap
numberOfTaps++
} else {
numberOfTaps = 1
}
this.lastTapUpEvent.copyFrom(event)
// Send event
val taps = numberOfTaps
handler.postDelayed({
// When this callback runs, we know if it is the final tap of a sequence
// if the number of taps has not changed
callback(taps, taps == numberOfTaps)
}, doubleTapTimeout)
}
}
}
true
}
}
}
答案 4 :(得分:-4)
Android不支持双击,这是有原因的。当您触摸Android屏幕时,Android会在短时间内向OS发送一系列触发器,表示x次,这被解释为单击。如果你进行双击,你将在几乎相同的时间跨度内再次发送相同的x +/- d个触发器。所以这两个水龙头都会混淆不清,而Android很难理解它是双击还是单击。因此它有onlongclick
听众。