Java 检测触摸按压与长按与移动?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4324362/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 15:53:29  来源:igfitidea点击:

Detect touch press vs long press vs movement?

javaandroidtouchmovelong-press

提问by pbean

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

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

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

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

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:

我用我自己的 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;
}

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.

我对此进行了一些“修改”,我引入了一个新变量“mTouchDelay”,我在 ACTION_DOWN 上将其设置为 0,增加 MOVE,如果它在 MOVE 中 >= 3,则执行我的“移动”代码。但我有一种感觉,这不是真正要走的路。

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

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

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.

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

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

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

回答by Jason L.

From the Android Docs -

来自 Android 文档 -

onLongClick()

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

来自 View.OnLongClickListener。当用户触摸并按住项目(在触摸模式下),或使用导航键或轨迹球聚焦在项目上并按住合适的“输入”键或按住轨迹球时,将调用此方法(一秒钟)。

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

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

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.

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

回答by Sanjay Manohar

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

回答by rajankz

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.

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

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

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

回答by Lord_JABA

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 androidsnippetsand then implement tap logic in onSingleTapUp and move logic in onScroll events

我想你应该实现GestureDetector.OnGestureListener中所述使用GestureDetector检测长触摸,双击,滚动或Android的其他触摸事件 androidsnippets,然后实现在onScroll事件onSingleTapUp分路的逻辑和逻辑的举动

回答by Chris Sullivan

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

在希望 longclick 不以点击事件结束后,我只是在处理这个烂摊子。

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.

这有点黑客,但它的工作原理。

回答by Nakul Sudhakar

it just needed a ontouch event in the overlay class. check this, it helped me

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

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

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

回答by MSquare

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

此代码可以区分单击和移动(拖动、滚动)。在 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;
}

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

对于上面建议的 LongPress,GestureDetector 是要走的路。检查此问答:

Detecting a long press with Android

使用 Android 检测长按

回答by voytez

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

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

Activity implements GestureDetector.OnGestureListener

活动器具 GestureDetector.OnGestureListener

then create detector in onCreate for example

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

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

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

然后可以选择 setOnTouchListener 在您的视图(例如 webview)上,其中

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)

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

回答by Subhanandh

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

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