68

我目前正在摆弄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 上运行。

4

9 回答 9

95

此代码可以区分单击和移动(拖动、滚动)。在onTouchEvent 中设置一个标志isOnClick,并将初始X、Y 坐标放在ACTION_DOWN 上。清除 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 是要走的路。检查此问答:

使用 Android 检测长按

于 2013-05-10T15:43:59.323 回答
22

来自 Android 文档 -

onLongClick()

来自 View.OnLongClickListener。当用户触摸并按住项目(处于触摸模式时)或使用导航键或轨迹球关注项目并按住合适的“输入”键或按住轨迹球时调用此方法(一秒钟)。

onTouch()

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

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

于 2010-12-01T13:10:49.433 回答
15

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

在初始化您的活动时:

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);
于 2012-02-15T03:25:50.493 回答
3

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

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

于 2012-07-02T04:57:21.850 回答
3

如果您需要区分单击、长按和滚动,请使用 GestureDetector

活动工具GestureDetector.OnGestureListener

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

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

然后在您的视图(例如 webview)上选择 setOnTouchListener

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

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

于 2014-04-10T10:02:42.320 回答
2

我认为您应该按照使用 GestureDetector 中所述实现 GestureDetector.OnGestureListener在 Android和 androidsnippets中检测 Long Touch、Double Tap、Scroll 或其他触摸事件, 然后在 onSingleTapUp 中实现点击逻辑并在 onScroll 事件中移动逻辑

于 2012-09-24T17:46:08.467 回答
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;

这有点骇人听闻,但它确实有效。

于 2012-09-25T21:39:47.270 回答
0

它只需要覆盖类中的 ontouch 事件。检查这个,它帮助了我

http://mirnauman.wordpress.com/2012/04/16/android-google-maps-tutorial-part-6-getting-the-location-that-is-touched/

于 2013-01-07T11:11:15.747 回答
0

GestureDetector.SimpleOnGestureListener 有方法可以帮助解决这 3 种情况;

   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);
        }
    });
于 2016-09-22T07:22:57.717 回答