简体   繁体   English

如何检测 onTouch 侦听器中的点击?

[英]How can I detect a click in an onTouch listener?

I have a ViewPager inside a ScrollView .我在ScrollView有一个ViewPager I need to be able to scroll horizontally as well as vertically.我需要能够水平和垂直滚动。 In order to achieve this had to disable the vertical scrolling whenever my ViewPager is touched ( v.getParent().requestDisallowInterceptTouchEvent(true); ), so that it can be scrolled horizontally.为了实现这一点,必须在我的ViewPager被触摸时禁用垂直滚动( v.getParent().requestDisallowInterceptTouchEvent(true); ),以便它可以水平滚动。

But at the same time I need to be able to click the viewPager to open it in full screen mode.但同时我需要能够单击 viewPager 以全屏模式打开它。

The problem is that onTouch gets called before onClick and my OnClick is never called.问题是 onTouch 在 onClick 之前被调用,而我的 OnClick 从未被调用。

How can I implement both on touch an onClick?如何在触摸时同时实现 onClick?

viewPager.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        System.out.println("TOUCHED ");
        if(event.getAction() == MotionEvent.???){
            //open fullscreen activity
        }
        v.getParent().requestDisallowInterceptTouchEvent(true); //This cannot be removed
        return false;
    }
});

viewPager.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        System.out.println("CLICKED ");
        Intent fullPhotoIntent = new Intent(context, FullPhotoActivity.class);
        fullPhotoIntent.putStringArrayListExtra("imageUrls", imageUrls);
        startActivity(fullPhotoIntent);
    }
});

Masoud Dadashi's answer helped me figure it out. Masoud Dadashi 的回答帮助我弄清楚了。

here is how it looks in the end.这就是它最终的样子。

viewPager.setOnTouchListener(new OnTouchListener() {
    private int CLICK_ACTION_THRESHOLD = 200;
    private float startX;
    private float startY;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX();
            startY = event.getY();
            break;
        case MotionEvent.ACTION_UP: 
            float endX = event.getX();
            float endY = event.getY();
            if (isAClick(startX, endX, startY, endY)) { 
                launchFullPhotoActivity(imageUrls);// WE HAVE A CLICK!!
            }
            break;
        }
        v.getParent().requestDisallowInterceptTouchEvent(true); //specific to my project
        return false; //specific to my project
    }

    private boolean isAClick(float startX, float endX, float startY, float endY) {
        float differenceX = Math.abs(startX - endX);
        float differenceY = Math.abs(startY - endY);
        return !(differenceX > CLICK_ACTION_THRESHOLD/* =5 */ || differenceY > CLICK_ACTION_THRESHOLD);
    } 
}

I did something really simple by checking the time the user touches the screen.通过检查用户触摸屏幕的时间,我做了一些非常简单的事情。

private static int CLICK_THRESHOLD = 100;

@Override
public boolean onTouch(View v, MotionEvent event) {
    long duration = event.getEventTime() - event.getDownTime();

    if (event.getAction() == MotionEvent.ACTION_UP && duration < CLICK_THRESHOLD) {
        Log.w("bla", "you clicked!");
    }

    return false;
}

Also worth noting that GestureDetector has something like this built-in.另外值得注意的是, GestureDetector内置了类似的东西。 Look at onSingleTapUp看看 onSingleTapUp

Developing both is the wrong idea.两者都发展是错误的想法。 when user may do different things by touching the screen understanding user purpose is a little bit nifty and you need to develop a piece of code for it.当用户可以通过触摸屏幕来做不同的事情时,理解用户目的有点漂亮,你需要为它开发一段代码。

Two solutions:两种解决方案:

1- (the better idea) in your onTouch event check if there is a motion. 1-(更好的主意)在您的 onTouch 事件中检查是否有动作。 You can do it by checking if there is any movement using:您可以通过检查是否有任何移动来做到这一点:

ACTION_UP
ACTION_DOWN
ACTION_MOVE

do it like this像这样做

if(event.getAction() != MotionEvent.ACTION_MOVE)

you can even check the distance of the movement of user finger on screen to make sure a movement happened rather than an accidental move while clicking.您甚至可以检查用户手指在屏幕上的移动距离,以确保发生了移动,而不是点击时的意外移动。 do it like this:像这样做:

switch(event.getAction())
 {
     case MotionEvent.ACTION_DOWN:
              if(isDown == false)
              {
                     startX = event.getX();
                     startY = event.getY();
                     isDown = true;
              }
              Break;
        case MotionEvent.ACTION_UP
              {
                     endX = event.getX();
                     endY = event.getY();
                     break;
          }
}

consider it a click if none of the above happened and do what you wanna do with click.如果上述情况均未发生,则将其视为一次单击,并通过单击执行您想做的事情。

2) if rimes with your UI, create a button or image button or anything for full screening and set an onClick for it. 2)如果你的 UI 有问题,创建一个按钮或图像按钮或任何用于全屏的东西,并为它设置一个 onClick。

Good luck祝你好运

You might need to differentiate between the user clicking and long-clicking.您可能需要区分用户单击和长按。 Otherwise, you'll detect both as the same thing.否则,您会将两者检测为同一事物。 I did this to make that possible:我这样做是为了使它成为可能:

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        startX = event.getX();
        startY = event.getY();

        bClick = true;
        tmrClick = new Timer();
        tmrClick.schedule(new TimerTask() {
            public void run() {
                if (bClick == true) {
                    bClick = false;
                    Log.d(LOG_TAG, "Hey, a long press event!");
                    //Handle the longpress event.
                }
            }
        }, 500); //500ms is the standard longpress response time. Adjust as you see fit.

        return true;
    case MotionEvent.ACTION_UP:
        endX = event.getX();
        endY = event.getY();

        diffX = Math.abs(startX - endX);
        diffY = Math.abs(startY - endY);

        if (diffX <= 5 && diffY <= 5 && bClick == true) {
            Log.d(LOG_TAG, "A click event!");
            bClick = false;
        }
        return true;
    default:
        return false;
    }
}

don't try to reinvent the wheel !不要试图重新发明轮子!

Elegant way to do it :优雅的方法:

public class CustomView extends View {

    private GestureDetectorCompat mDetector;

    public CustomView(Context context) {
        super(context);
        mDetector = new GestureDetectorCompat(context, new MyGestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        return this.mDetector.onTouchEvent(event);
    }

    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent e) {return true;}

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            //...... click detected !
            return false;
        }
    }
}

The answers above mostly memorize the time.上面的答案主要是记住时间。 However, MotionEvent already has you covered.但是, MotionEvent已经涵盖了您。 Here a solution with less overhead.这是一个开销较小的解决方案。 Its written in kotlin but it should still be understandable:它是用kotlin写的,但应该还是可以理解的:

private const val ClickThreshold = 100

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if(event.action == MotionEvent.ACTION_UP 
      && event.eventTime - event.downTime < ClickThreshold) {
        v.performClick()
        return true // If you don't want to do any more actions
    }

    // do something in case its not a click

    return true // or false, whatever you need here
}

This solution is good enough for most applications but is not so good in distinguishing between a click and a very quick swipe.这个解决方案对于大多数应用程序来说已经足够好了,但在区分点击和快速滑动方面不太好。

So, combining this with the answers above that also take the position into account is probably the best one.因此,将其与上面的答案结合起来,也考虑到这个位置可能是最好的答案。

I think combined solution time/position should be better:我认为组合解决方案时间/位置应该更好:

 private float initialTouchX;
 private float initialTouchY;
 private long lastTouchDown;
 private static int CLICK_ACTION_THRESHHOLD = 100;
 @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        lastTouchDown = System.currentTimeMillis();

                        //get the touch location
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return true;
                    case MotionEvent.ACTION_UP:
                        int Xdiff = (int) (event.getRawX() - initialTouchX);
                        int Ydiff = (int) (event.getRawY() - initialTouchY);

                        if (System.currentTimeMillis() - lastTouchDown < CLICK_ACTION_THRESHHOLD &&  (Xdiff < 10 && Ydiff < 10))  {
                           //clicked!!!
                        }
                        return true;
                }
                return false;
            }
        });

I believe you're preventing your view from receiving the touch event this way because your TouchListener intercepts it.我相信您会阻止您的视图以这种方式接收触摸事件,因为您的 TouchListener 会拦截它。 You can either你可以

  • Dispatch the event inside your ToucheListener by calling v.onTouchEvent(event)通过调用v.onTouchEvent(event)在 ToucheListener 中调度事件
  • Override instead ViewPager.onTouchEvent(MotionEvent) not to intercept the event改为覆盖ViewPager.onTouchEvent(MotionEvent)不拦截事件

Also, returning true means that you didn't consume the event, and that you're not interrested in following events, and you won't therefore receive following events until the gesture completes (that is, the finger is up again).此外,返回true意味着您没有消费该事件,并且您对后续事件不感兴趣,因此在手势完成之前您不会收到后续事件(即手指再次向上)。

you can use OnTouchClickListener你可以使用 OnTouchClickListener

https://github.com/hoffergg/BaseLibrary/blob/master/src/main/java/com/dailycation/base/view/listener/OnTouchClickListener.java https://github.com/hoffergg/BaseLibrary/blob/master/src/main/java/com/dailycation/base/view/listener/OnTouchClickListener.java

usage:用法:

 view.setOnTouchListener(new OnTouchClickListener(new OnTouchClickListener.OnClickListener() { @Override public void onClick(View v) { //perform onClick } },5));

if (event.eventTime - event.downTime < 100 && event.actionMasked == MotionEvent.ACTION_UP) {
     view.performClick()
     return false
}

This code will do both touch events and click event.此代码将执行触摸事件和单击事件。

viewPager.setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;


            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        //remember the initial position.
                        initialX = params.x;
                        initialY = params.y;

                        //get the touch location
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return true;
                    case MotionEvent.ACTION_UP:
                        int Xdiff = (int) (event.getRawX() - initialTouchX);
                        int Ydiff = (int) (event.getRawY() - initialTouchY);


                        //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                        
                        if (Xdiff < 10 && Ydiff < 10) {
                            //So that is click event.
                        }
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        //Calculate the X and Y coordinates of the view.
                        params.x = initialX + (int) (event.getRawX() - initialTouchX);
                        params.y = initialY + (int) (event.getRawY() - initialTouchY);


                        //Update the layout with new X & Y coordinate
                        mWindowManager.updateViewLayout(mFloatingView, params);
                        return true;
                }
                return false;
            }
        });

Here is the source.这是来源。

I think your problem comes from the line:我认为你的问题来自于:

v.getParent().requestDisallowInterceptTouchEvent(true); //This cannot be removed

Take a look to the documentation .看看文档 Have you tried to remove the line?你试过删除线吗? What is the requeriment for not removing this line?不删除此行的要求是什么?

Take into account, according to the documentation , that if you return true from your onTouchListener, the event is consumed, and if you return false, is propagated, so you could use this to propagate the event.考虑到,根据文档,如果您从 onTouchListener 返回 true,则使用该事件,如果您返回 false,则传播该事件,因此您可以使用它来传播该事件。

Also, you should change your code from:此外,您应该从以下位置更改您的代码:

 System.out.println("CLICKED ");

to:至:

 Log.d("MyApp", "CLICKED ");

To get correct output in logcat.在 logcat 中获得正确的输出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何设置“项目点击侦听器”上的视图寻呼机? 设置点击监听器不起作用 - How can I set view pager's on Item Click Listener? Set On Click Listener is not working 如何在jTable单元格的jCombobox中添加鼠标单击侦听器? - How can i add a mouse click listener to a jCombobox in a jTable cell? 如何为 AppWidgetHostView 制作长按侦听器 - How can I make a long click listener for an AppWidgetHostView 如何为 ImageButton 同时获得 OnClick 和 OnTouch 侦听器? - How can i get both OnClick and OnTouch Listeners for an ImageButton? 如何通过鼠标移动检测 JLabel 中的点击? (比如 JButton)? - How can I detect a click in a JLabel with mouse movement? (like JButton)? 如何在Slick2D中检测到旋转图像上的单击? - How can I detect a click on a rotated image in Slick2D? 如何检测Android中外部应用程序的点击事件? - How can I detect click events of external application in Android? 如何检测 Android 中物理键的双击?(onKeyMultiple) - How can I detect double click with physical key in Android?(onKeyMultiple) 我如何检测JavaFx双击listView - How can I detect JavaFx double click on listView 如何向CardView添加单击侦听器? - How do I add a click listener to a CardView?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM