[英]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
你可以
v.onTouchEvent(event)
v.onTouchEvent(event)
在 ToucheListener 中调度事件ViewPager.onTouchEvent(MotionEvent)
not to intercept the eventViewPager.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;
}
});
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.