检测触摸按压与长按vs运动?

时间:2010-12-01 12:31:23

标签: java android touch move long-press

我目前正在摆弄Android编程,但我在检测不同的触摸事件时遇到一个小问题,即正常触摸按下(按屏幕并立即释放),长按(触摸屏幕并按住手指放在上面)和动作(在屏幕上拖动)。

我想要做的是在屏幕上有一个图像(圆圈),我可以拖动它。然后,当我按一次(短/正常按下)时,Toast会提供一些关于它的基本信息。当我长按它时,会出现一个带有列表的AlertDialog来选择不同的图像(圆形,矩形或三角形)。

我使用自己的OnTouchListener创建了一个自定义视图来检测事件并在onDraw中绘制图像。 OnTouchListener.onTouch是这样的:

// has a touch press started?
private boolean touchStarted = false;
// co-ordinates of image
private int x, y;

public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        touchStarted = true;
    }
    else if (action == MotionEvent.ACTION_MOVE) {
        // movement: cancel the touch press
        touchStarted = false;

        x = event.getX();
        y = event.getY();

        invalidate(); // request draw
    }
    else if (action == MotionEvent.ACTION_UP) {
        if (touchStarted) {
            // touch press complete, show toast
            Toast.makeText(v.getContext(), "Coords: " + x + ", " + y, 1000).show();
        }
    }

    return true;
}

问题在于印刷机没有按预期工作,因为当我随意触摸屏幕时,它还会检测到一点点移动并取消触摸按压并在图像周围移动。

我“围攻”了一下我引入了一个新的变量“mTouchDelay”,我在ACTION_DOWN上设置为0,在MOVE中增加,如果在MOVE中它是> = 3,我执行我的“移动”代码。但我觉得这不是真正的方法。

我还没有发现如何检测长按。罪魁祸首真的是MOVE,似乎总是触发。

有关我大概需要的示例,请参阅Android应用程序“DailyStrip”:它显示漫画的图像。如果它对于屏幕而言太大,您可以拖动它。您可以点击一次以使某些控件弹出,并长按它以显示选项菜单。

PS。我试图让它在Android 1.5上运行,因为我的手机只运行1.5。

9 个答案:

答案 0 :(得分:89)

此代码可以区分点击和移动(拖动,滚动)。在onTouchEvent中设置一个标志isOnClick,并在ACTION_DOWN上设置初始X,Y坐标。清除ACTION_MOVE上的标志(注意经常检测到无意识的移动,这可以通过THRESHOLD const来解决)。

private float mDownX;
private float mDownY;
private final float SCROLL_THRESHOLD = 10;
private boolean isOnClick;

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mDownX = ev.getX();
            mDownY = ev.getY();
            isOnClick = true;
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (isOnClick) {
                Log.i(LOG_TAG, "onClick ");
                //TODO onClick code
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (isOnClick && (Math.abs(mDownX - ev.getX()) > SCROLL_THRESHOLD || Math.abs(mDownY - ev.getY()) > SCROLL_THRESHOLD)) {
                Log.i(LOG_TAG, "movement detected");
                isOnClick = false;
            }
            break;
        default:
            break;
    }
    return true;
}

对于LongPress,如上所述,GestureDetector是要走的路。检查此Q& A:

Detecting a long press with Android

答案 1 :(得分:20)

来自Android文档 -

onLongClick()

来自View.OnLongClickListener。当用户触摸并保持项目时(当处于触摸模式时)或者使用导航键或轨迹球聚焦于项目并按下并按住合适的“输入”键或按下并按住轨迹球时(一秒钟。

onTouch()

来自View.OnTouchListener。当用户执行限定为触摸事件的操作时调用此方法,包括按下,释放或屏幕上的任何移动手势(在项目的范围内)。

至于“即使我触摸时也会发生移动”,我会设置一个增量并确保在移动代码之前,视图已被移动了至少三角形。如果还没有,请启动触摸代码。

答案 2 :(得分:14)

经过大量的实验,我发现了这一点。

在您的活动初始化中:

setOnLongClickListener(new View.OnLongClickListener() {
  public boolean onLongClick(View view) {
    activity.openContextMenu(view);  
    return true;  // avoid extra click events
  }
});
setOnTouch(new View.OnTouchListener(){
  public boolean onTouch(View v, MotionEvent e){
    switch(e.getAction & MotionEvent.ACTION_MASK){
      // do drag/gesture processing. 
    }
    // you MUST return false for ACTION_DOWN and ACTION_UP, for long click to work
    // you can return true for ACTION_MOVEs that you consume. 
    // DOWN/UP are needed by the long click timer.
    // if you want, you can consume the UP if you have made a drag - so that after 
    // a long drag, no long-click is generated.
    return false;
  }
});
setLongClickable(true);

答案 3 :(得分:3)

我正在寻找类似的解决方案,这就是我的建议。 在OnTouch方法中,记录MotionEvent.ACTION_DOWN事件的时间,然后为MotionEvent.ACTION_UP记录时间。这样您也可以设置自己的阈值。经过几次实验后,您将知道记录简单触摸所需的最大时间(毫秒),您可以根据自己的喜好使用移动或其他方法。

希望这有帮助。如果你使用了不同的方法并解决了你的问题,请发表评论。

答案 4 :(得分:3)

如果你需要在点击,长按和滚动之间分开使用GestureDetector

活动实施GestureDetector.OnGestureListener

然后在onCreate中创建检测器,例如

mDetector = new GestureDetectorCompat(getActivity().getApplicationContext(),this);

然后在View上选择setOnTouchListener(例如webview)

onTouch(View v, MotionEvent event) {
return mDetector.onTouchEvent(event);
}

现在你可以使用Override onScroll,onFling,showPress(检测长按)或onSingleTapUp(检测点击)

答案 5 :(得分:2)

我认为你应该实现GestureDetector.OnGestureListener 如Using GestureDetector to detect Long Touch, Double Tap, Scroll or other touch events in Android androidsnippets中所述 {{3}} 然后在onSingleTapUp中实现tap逻辑并在onScroll事件中移动逻辑

答案 6 :(得分:0)

我想在longclick没有以点击事件结束后处理这个烂摊子。

这就是我的所作所为。

public boolean onLongClick(View arg0) {
    Toast.makeText(getContext(), "long click", Toast.LENGTH_SHORT).show();
    longClicked = true;
    return false;
}

public void onClick(View arg0) {
    if(!longClicked){
        Toast.makeText(getContext(), "click", Toast.LENGTH_SHORT).show();
    }
    longClick = false; // sets the clickability enabled
}

boolean longClicked = false;

这有点像黑客但它有效。

答案 7 :(得分:0)

答案 8 :(得分:0)

GestureDetector.SimpleOnGestureListener有三种方法可以提供帮助;

   GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {

        //for single click event.
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return true;
        }

        //for detecting a press event. Code for drag can be added here.
        @Override
        public void onShowPress(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clipData = ClipData.newPlainText("..", "...");
            clipboardManager.setPrimaryClip(clipData);

            ConceptDragShadowBuilder dragShadowBuilder = new CustomDragShadowBuilder(child);
            // drag child view.
            child.startDrag(clipData, dragShadowBuilder, child, 0);
        }

        //for detecting longpress event
        @Override
        public void onLongPress(MotionEvent e) {
            super.onLongPress(e);
        }
    });