检测触摸按下、长按还是移动?

发布于 2024-10-05 19:04:28 字数 1473 浏览 7 评论 0原文

我目前正在摆弄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,则执行我的“移动”代码。但我有一种感觉,这并不是真正的出路。

我还没有找到如何检测长按。罪魁祸首确实是似乎总是触发的移动。

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

附言。我正在尝试让它在 Android 1.5 上运行,因为我的手机只能在 1.5 上运行。

I'm currently fiddling around with Android programming, but I have a small problem detecting different touch events, namely a normal touch press (press on the screen and release right away), a long press (touch the screen and hold the finger on it) and movement (dragging on the screen).

What I wanted to do is have an image (of a circle) on my screen which I can drag around. Then when I press it once (short/normal press) a Toast comes up with some basic information about it. When I long press it, an AlertDialog with a list comes up to select a different image (circle, rectangle or triangle).

I made a custom View with my own OnTouchListener to detect the events and draw the image in onDraw. The OnTouchListener.onTouch goes something like this:

// 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;
}

The problem is that the press doesn't quite work as expected, because when I casually touch the screen it also detects a tiny bit of movement and cancels the touch press and moves around the image instead.

I "hacked" around this a bit my introducing a new variable "mTouchDelay" which I set to 0 on ACTION_DOWN, increase in MOVE and if it's >= 3 in MOVE I execute my "move" code. But I have a feeling this isn't really the way to go.

I also haven't found out how to detect a long press. The culprit really is the MOVE which seems to always trigger.

For an example of what I roughly want, see the Android application "DailyStrip": it shows an image of a comic strip. You can drag it if it's too large for the screen. You can tap it once for some controls to pop-up and long press it for an options menu.

PS. I'm trying to get it to work on Android 1.5, since my phone only runs on 1.5.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

孤独陪着我 2024-10-12 19:04:28

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

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 检测长按

This code can distinguish between click and movement (drag, scroll). In onTouchEvent set a flag isOnClick, and initial X, Y coordinates on ACTION_DOWN. Clear the flag on ACTION_MOVE (minding that unintentional movement is often detected which can be solved with a 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;
}

For LongPress as suggested above, GestureDetector is the way to go. Check this Q&A:

Detecting a long press with Android

笑,眼淚并存 2024-10-12 19:04:28

来自 Android 文档 -

onLongClick()

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

onTouch()

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

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

From the Android Docs -

onLongClick()

From View.OnLongClickListener. This is called when the user either touches and holds the item (when in touch mode), or focuses upon the item with the navigation-keys or trackball and presses and holds the suitable "enter" key or presses and holds down on the trackball (for one second).

onTouch()

From View.OnTouchListener. This is called when the user performs an action qualified as a touch event, including a press, a release, or any movement gesture on the screen (within the bounds of the item).

As for the "moving happens even when I touch" I would set a delta and make sure the View has been moved by at least the delta before kicking in the movement code. If it hasn't been, kick off the touch code.

你丑哭了我 2024-10-12 19:04:28

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

在您的活动初始化时:

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);

I discovered this after a lot of experimentation.

In the initialisation of your activity:

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);
吖咩 2024-10-12 19:04:28

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

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

I was looking for a similar solution and this is what I would suggest.
In the OnTouch method, record the time for MotionEvent.ACTION_DOWN event and then for MotionEvent.ACTION_UP, record the time again. This way you can set your own threshold also. After experimenting few times you will know the max time in millis it would need to record a simple touch and you can use this in move or other method as you like.

Hope this helped. Please comment if you used a different method and solved your problem.

漆黑的白昼 2024-10-12 19:04:28

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

Activity 实现 GestureDetector.OnGestureListener

然后在 onCreate 中创建检测器,

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

然后可以选择在您的视图(例如 webview)上设置 OnTouchListener

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

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

If you need to distniguish between a click, longpress and a scroll use GestureDetector

Activity implements GestureDetector.OnGestureListener

then create detector in onCreate for example

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

then optionally setOnTouchListener on your View (for example webview) where

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

and now you can use Override onScroll, onFling, showPress( detect long press) or onSingleTapUp (detect a click)

浅笑依然 2024-10-12 19:04:28

我认为你应该实现 GestureDetector.OnGestureListener
使用 GestureDetector 检测 Android 中的长按、双击、滚动或其他触摸事件

androidsnippets
然后在 onSingleTapUp 中实现点击逻辑并在 onScroll 事件中实现移动逻辑

I think you should implement GestureDetector.OnGestureListener
as described in Using GestureDetector to detect Long Touch, Double Tap, Scroll or other touch events in Android
and
androidsnippets
and then implement tap logic in onSingleTapUp and move logic in onScroll events

昔日梦未散 2024-10-12 19:04:28

我只是在希望 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;

这有点麻烦,但它确实有效。

I was just dealing with this mess after wanting longclick to not end with a click event.

Here's what I did.

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;

It's a bit of a hack but it works.

二智少女 2024-10-12 19:04:28

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);
        }
    });

GestureDetector.SimpleOnGestureListener has methods to help in these 3 cases;

   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);
        }
    });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文