39

我有一个ViewPager里面的ScrollView。我需要能够水平和垂直滚动。为了实现这一点,必须在ViewPager触摸 my 时禁用垂直滚动 ( v.getParent().requestDisallowInterceptTouchEvent(true);),以便它可以水平滚动。

但同时我需要能够单击 viewPager 以全屏模式打开它。

问题是 onTouch 在 onClick 之前被调用,而我的 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);
    }
});
4

12 回答 12

72

Masoud Dadashi 的回答帮助我弄清楚了。

这就是它最终的样子。

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);
    } 
}
于 2013-07-24T13:54:33.130 回答
37

通过检查用户触摸屏幕的时间,我做了一些非常简单的事情。

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

另外值得注意的是,GestureDetector内置了类似的东西。看看 onSingleTapUp

于 2015-06-10T08:40:19.767 回答
10

两者都发展是错误的想法。当用户可以通过触摸屏幕来做不同的事情时,理解用户目的有点漂亮,你需要为它开发一段代码。

两种解决方案:

1-(更好的主意)在您的 onTouch 事件中检查是否有动作。您可以通过检查是否有任何移动来做到这一点:

ACTION_UP
ACTION_DOWN
ACTION_MOVE

像这样做

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

您甚至可以检查用户手指在屏幕上的移动距离,以确保发生了移动,而不是点击时的意外移动。像这样做:

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

如果上述情况均未发生,则将其视为一次单击,并通过单击执行您想做的事情。

2)如果你的 UI 有问题,创建一个按钮或图像按钮或任何用于全屏的东西,并为它设置一个 onClick。

祝你好运

于 2013-07-24T11:07:36.357 回答
5

您可能需要区分用户单击和长按。否则,您会将两者检测为同一事物。我这样做是为了使它成为可能:

@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;
    }
}
于 2014-12-15T23:32:09.173 回答
5

优雅的方式

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;
        }
    }
}
于 2020-02-17T16:43:35.117 回答
4

上面的答案主要是记住时间。但是,MotionEvent已经涵盖了您。这是一个开销较小的解决方案。它写在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
}

这个解决方案对于大多数应用程序来说已经足够好了,但在区分点击和快速滑动方面不太好。

因此,将其与上面的答案结合起来,也考虑到这个位置可能是最好的答案。

于 2019-03-19T15:52:59.177 回答
2

我认为组合解决方案时间/位置应该更好:

 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;
            }
        });
于 2017-03-28T11:09:48.250 回答
1

我相信您会阻止您的视图以这种方式接收触摸事件,因为您的 TouchListener 会拦截它。你可以

  • 通过调用在 ToucheListener 中调度事件v.onTouchEvent(event)
  • 覆盖而ViewPager.onTouchEvent(MotionEvent)不是拦截事件

此外,返回true意味着您没有消费该事件,并且您对后续事件不感兴趣,因此在手势完成之前您不会收到后续事件(即手指再次向上)。

于 2013-07-24T10:39:53.467 回答
1

你可以使用 OnTouchClickListener

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

用法:

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

于 2017-03-15T16:14:35.607 回答
1
if (event.eventTime - event.downTime < 100 && event.actionMasked == MotionEvent.ACTION_UP) {
     view.performClick()
     return false
}
于 2021-01-11T16:35:46.500 回答
0

我认为你的问题来自于:

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

看看文档。你试过删除线吗?不删除此行的要求是什么?

考虑到,根据文档,如果您从 onTouchListener 返回 true,则使用该事件,如果您返回 false,则传播该事件,因此您可以使用它来传播该事件。

此外,您应该从以下位置更改您的代码:

 System.out.println("CLICKED ");

到:

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

在 logcat 中获得正确的输出。

于 2013-07-24T10:39:15.297 回答
0

此代码将执行触摸事件和单击事件。

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

这是来源。

于 2021-08-04T18:32:18.100 回答