在网格布局上进行手势检测
我想要在我的Android应用程序中工作。
我有一个包含9个ImageView
的GridLayout
。 来源可以在这里find: 罗曼人的网格布局 。
我拿的文件是从罗曼盖伊的Photostream应用程序 ,只有轻微的适应。
对于简单的点击情况,我只需要将每个添加的ImageView
View.OnClickListener
设置为实现View.OnClickListener
的主要activity
。 实施一些能够识别fling
东西似乎是无限复杂的。 我认为这是因为它可能会跨越views
?
-
如果我的活动实现
OnGestureListener
我不知道如何将其设置为我添加的Grid
或Image
视图的手势监听器。public class SelectFilterActivity extends Activity implements View.OnClickListener, OnGestureListener { ...
-
如果我的活动实现
OnTouchListener
那么我没有onFling
方法来override
(它有两个事件作为参数,让我可以确定是否值得注意)。public class SelectFilterActivity extends Activity implements View.OnClickListener, OnTouchListener { ...
-
如果我做一个自定义的
View
,就像扩展ImageView
GestureImageView
我不知道如何告诉活动,从视图中发生了fling
。 无论如何,我试过这个,当我触摸屏幕的时候,方法并没有被调用。
我真的只是需要一个具体的例子,跨视图工作。 什么时候以及如何附加这个listener
? 我需要能够检测到单击也。
// Gesture detection mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int dx = (int) (e2.getX() - e1.getX()); // don't accept the fling if it's too short // as it may conflict with a button push if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.absvelocityY)) { if (velocityX > 0) { moveRight(); } else { moveLeft(); } return true; } else { return false; } } });
是否有可能在我的屏幕顶部放置一个透明的视图来捕捉掠过?
如果我select不inflate
我的孩子图像视图从XML我可以将GestureDetector
作为构造函数parameter passing给我创build的ImageView
一个新的子类?
这是非常简单的活动,我试图让工作检测的工作: SelectFilterActivity(从照片stream改编) 。
我一直在看这些来源:
-
检测手势 – 教程
-
SDK文档
-
计算器代码
到目前为止,没有任何工作为我工作,我希望得到一些指示。
感谢Code Shogun ,它的代码适应了我的情况。
让你的活动像往常一样实现OnClickListener
:
public class SelectFilterActivity extends Activity implements OnClickListener { private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; private GestureDetector gestureDetector; View.OnTouchListener gestureListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* ... */ // Gesture detection gestureDetector = new GestureDetector(this, new MyGestureDetector()); gestureListener = new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }; } class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { try { if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) return false; // right to left swipe if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show(); } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { // nothing } return false; } @Override public boolean onDown(MotionEvent e) { return true; } } }
将您的手势监听器附加到您添加到主布局的所有视图;
// Do this for each view added to the grid imageView.setOnClickListener(SelectFilterActivity.this); imageView.setOnTouchListener(gestureListener);
onFling
关注重写的方法,包括活动的onClick(View v)
和手势监听器的onFling
。
public void onClick(View v) { Filter f = (Filter) v.getTag(); FilterFullscreenActivity.show(this, input, f); }
“扔”舞是可选的,但鼓励。
上面的答案之一提到处理不同的像素密度,但build议手动计算滑动参数。 值得注意的是,您可以使用ViewConfiguration
类从系统中实际获得缩放的合理值:
final ViewConfiguration vc = ViewConfiguration.get(getContext()); final int swipeMinDistance = vc.getScaledPagingTouchSlop(); final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity(); final int swipeMaxOffPath = vc.getScaledTouchSlop(); // (there is also vc.getScaledMaximumFlingVelocity() one could check against)
我注意到,使用这些值会导致应用程序和系统其余部分之间的“投掷”感觉更加一致。
我做了一些不同,并写了一个额外的检测器类,实现了View.onTouchListener
onCreate
只是简单地将它添加到像这样的最低布局:
ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this); lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout); lowestLayout.setOnTouchListener(activitySwipeDetector);
其中id.lowestLayout是布局层次结构中视图最低的id.xxx,而lowestLayout则声明为RelativeLayout
然后是实际的活动检测器类:
public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private Activity activity; static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public ActivitySwipeDetector(Activity activity){ this.activity = activity; } public void onRightSwipe(){ Log.i(logTag, "RightToLeftSwipe!"); activity.doSomething(); } public void onLeftSwipe(){ Log.i(logTag, "LeftToRightSwipe!"); activity.doSomething(); } public void onDownSwipe(){ Log.i(logTag, "onTopToBottomSwipe!"); activity.doSomething(); } public void onUpSwipe(){ Log.i(logTag, "onBottomToTopSwipe!"); activity.doSomething(); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > Math.abs(deltaY)) { if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX > 0) { this.onRightSwipe(); return true; } if(deltaX < 0) { this.onLeftSwipe(); return true; } } else { Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); return false; // We don't consume the event } } // swipe vertical? else { if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onDownSwipe(); return true; } if(deltaY > 0) { this.onUpSwipe(); return true; } } else { Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); return false; // We don't consume the event } } return true; } } return false; } }
对我真的很好!
我稍微修改和修复了Thomas Fankhauser的解决scheme
整个系统由两个文件SwipeInterface和ActivitySwipeDetector组成
SwipeInterface.java
import android.view.View; public interface SwipeInterface { public void bottom2top(View v); public void left2right(View v); public void right2left(View v); public void top2bottom(View v); }
探测器
import android.util.Log; import android.view.MotionEvent; import android.view.View; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private SwipeInterface activity; static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public ActivitySwipeDetector(SwipeInterface activity){ this.activity = activity; } public void onRightToLeftSwipe(View v){ Log.i(logTag, "RightToLeftSwipe!"); activity.right2left(v); } public void onLeftToRightSwipe(View v){ Log.i(logTag, "LeftToRightSwipe!"); activity.left2right(v); } public void onTopToBottomSwipe(View v){ Log.i(logTag, "onTopToBottomSwipe!"); activity.top2bottom(v); } public void onBottomToTopSwipe(View v){ Log.i(logTag, "onBottomToTopSwipe!"); activity.bottom2top(v); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); } // swipe vertical? if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; } if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; } } else { Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); v.performClick(); } } } return false; } }
它是这样使用的:
ActivitySwipeDetector swipe = new ActivitySwipeDetector(this); LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout); swipe_layout.setOnTouchListener(swipe);
而在实现Activity
您需要从SwipeInterface实现方法,并且可以找出在哪个视图中调用了Swipe事件 。
@Override public void left2right(View v) { switch(v.getId()){ case R.id.swipe_layout: // do your stuff here break; } }
上面的滑动手势检测器代码非常有用! 但是,您可能希望通过使用以下相对值(REL_SWIPE)
而不是绝对值(SWIPE_)
来使此解决scheme密度不可知
DisplayMetrics dm = getResources().getDisplayMetrics(); int REL_SWIPE_MIN_DISTANCE = (int)(SWIPE_MIN_DISTANCE * dm.densityDpi / 160.0f); int REL_SWIPE_MAX_OFF_PATH = (int)(SWIPE_MAX_OFF_PATH * dm.densityDpi / 160.0f); int REL_SWIPE_THRESHOLD_VELOCITY = (int)(SWIPE_THRESHOLD_VELOCITY * dm.densityDpi / 160.0f);
我的解决scheme由Thomas Fankhauser和Marek Sebera提出 (不处理垂直滑动):
SwipeInterface.java
import android.view.View; public interface SwipeInterface { public void onLeftToRight(View v); public void onRightToLeft(View v); }
ActivitySwipeDetector.java
import android.content.Context; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private SwipeInterface activity; private float downX, downY; private long timeDown; private final float MIN_DISTANCE; private final int VELOCITY; private final float MAX_OFF_PATH; public ActivitySwipeDetector(Context context, SwipeInterface activity){ this.activity = activity; final ViewConfiguration vc = ViewConfiguration.get(context); DisplayMetrics dm = context.getResources().getDisplayMetrics(); MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density; VELOCITY = vc.getScaledMinimumFlingVelocity(); MAX_OFF_PATH = MIN_DISTANCE * 2; } public void onRightToLeftSwipe(View v){ Log.i(logTag, "RightToLeftSwipe!"); activity.onRightToLeft(v); } public void onLeftToRightSwipe(View v){ Log.i(logTag, "LeftToRightSwipe!"); activity.onLeftToRight(v); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { Log.d("onTouch", "ACTION_DOWN"); timeDown = System.currentTimeMillis(); downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { Log.d("onTouch", "ACTION_UP"); long timeUp = System.currentTimeMillis(); float upX = event.getX(); float upY = event.getY(); float deltaX = downX - upX; float absDeltaX = Math.abs(deltaX); float deltaY = downY - upY; float absDeltaY = Math.abs(deltaY); long time = timeUp - timeDown; if (absDeltaY > MAX_OFF_PATH) { Log.i(logTag, String.format("absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH)); return v.performClick(); } final long M_SEC = 1000; if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) { if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, String.format("absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE))); Log.i(logTag, String.format("absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC))); } } } return false; } }
这个问题有点古怪,2011年7月,Google发布了兼容性套件,第3版) ,其中包括与Android 1.6以上版本的ViewPager
。 为这个问题发布的GestureListener
答案在Android上感觉不太优雅。 如果您正在查找用于在Android图库中切换照片的代码或在新的Play Market应用程序中切换视图,那么它绝对是ViewPager
。
以下是更多信息的链接:
有一个内置的界面,您可以直接使用所有的手势:
这是一个基本级别用户的解释: 有两个import小心select,两者是不同的
如果有人想要一个工作的实现,这是两个答案的综合答案。
package com.yourapplication; import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public abstract class OnSwipeListener implements View.OnTouchListener { private final GestureDetector gestureDetector; public OnSwipeListener(Context context){ gestureDetector = new GestureDetector(context, new OnSwipeGestureListener(context)); gestureDetector.setIsLongpressEnabled(false); } @Override public boolean onTouch(View view, MotionEvent event) { return gestureDetector.onTouchEvent(event); } private final class OnSwipeGestureListener extends GestureDetector.SimpleOnGestureListener { private final int minSwipeDelta; private final int minSwipeVelocity; private final int maxSwipeVelocity; private OnSwipeGestureListener(Context context) { ViewConfiguration configuration = ViewConfiguration.get(context); // We think a swipe scrolls a full page. //minSwipeDelta = configuration.getScaledTouchSlop(); minSwipeDelta = configuration.getScaledPagingTouchSlop(); minSwipeVelocity = configuration.getScaledMinimumFlingVelocity(); maxSwipeVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override public boolean onDown(MotionEvent event) { // Return true because we want system to report subsequent events to us. return true; } // NOTE: see http://stackoverflow.com/questions/937313/android-basic-gesture-detection @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { boolean result = false; try { float deltaX = event2.getX() - event1.getX(); float deltaY = event2.getY() - event1.getY(); float absVelocityX = Math.abs(velocityX); float absVelocityY = Math.abs(velocityY); float absDeltaX = Math.abs(deltaX); float absDeltaY = Math.abs(deltaY); if (absDeltaX > absDeltaY) { if (absDeltaX > minSwipeDelta && absVelocityX > minSwipeVelocity && absVelocityX < maxSwipeVelocity) { if (deltaX < 0) { onSwipeLeft(); } else { onSwipeRight(); } } result = true; } else if (absDeltaY > minSwipeDelta && absVelocityY > minSwipeVelocity && absVelocityY < maxSwipeVelocity) { if (deltaY < 0) { onSwipeTop(); } else { onSwipeBottom(); } } result = true; } catch (Exception e) { e.printStackTrace(); } return result; } } public void onSwipeLeft() {} public void onSwipeRight() {} public void onSwipeTop() {} public void onSwipeBottom() {} }
也作为一个小小的增强。
try / catch块的主要原因是e1对于初始移动可能是空的。 除了try / catch之外,还包括一个null和return的testing。 类似于以下内容
if (e1 == null || e2 == null) return false; try { ... } catch (Exception e) {} return false;
这里有很多优秀的信息。 不幸的是,许多这种投掷处理代码散布在各种完成状态的网站上,尽pipe人们会认为这对许多应用程序是必不可less的。
我花时间创build了一个可以validation合适的条件得到满足的投掷者监听器 。 我已经添加了一个页面跳动侦听器 ,添加更多的检查,以确保翻身满足翻页的阈值。 这两个听众都可以让您轻松地将甩动限制在水平轴或垂直轴上。 你可以看到它是如何用于滑动图像的视图 。 我承认,这里的人做了大部分的研究 – 我把它们放在一个可用的图书馆里。
这些日子代表了我第一次在Android上编码; 期待更多的来。
您可以使用droidQuery库来处理掠过 ,点击,长时间点击和自定义事件。 这个实现是build立在我之前的回答下面,但是droidQuery提供了一个简洁的语法:
//global variables private boolean isSwiping = false; private SwipeDetector.Direction swipeDirection = null; private View v;//must be instantiated before next call. //swipe-handling code $.with(v).swipe(new Function() { @Override public void invoke($ droidQuery, Object... params) { if (params[0] == SwipeDetector.Direction.START) isSwiping = true; else if (params[0] == SwipeDetector.Direction.STOP) { if (isSwiping) { isSwiping = false; if (swipeDirection != null) { switch(swipeDirection) { case DOWN : //TODO: Down swipe complete, so do something break; case UP : //TODO: Up swipe complete, so do something break; case LEFT : //TODO: Left swipe complete, so do something break; case RIGHT : //TODO: Right swipe complete, so do something break; default : break; } } } } else { swipeDirection = (SwipeDetector.Direction) params[0]; } } });
原始答复
这个答案使用了来自其他答案的组件的组合。 它由SwipeDetector
类组成,它有一个用于监听事件的内部接口。 我还提供了一个RelativeLayout
来展示如何覆盖View
的onTouch
方法,以允许滑动事件和其他检测到的事件(如点击或长时间点击)。
SwipeDetector
package self.philbrown; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Detect Swipes on a per-view basis. Based on original code by Thomas Fankhauser on StackOverflow.com, * with adaptations by other authors (see link). * @author Phil Brown * @see <a href="http://stackoverflow.com/questions/937313/android-basic-gesture-detection">android-basic-gesture-detection</a> */ public class SwipeDetector implements View.OnTouchListener { /** * The minimum distance a finger must travel in order to register a swipe event. */ private int minSwipeDistance; /** Maintains a reference to the first detected down touch event. */ private float downX, downY; /** Maintains a reference to the first detected up touch event. */ private float upX, upY; /** provides access to size and dimension contants */ private ViewConfiguration config; /** * provides callbacks to a listener class for various swipe gestures. */ private SwipeListener listener; public SwipeDetector(SwipeListener listener) { this.listener = listener; } /** * {@inheritDoc} */ public boolean onTouch(View v, MotionEvent event) { if (config == null) { config = ViewConfiguration.get(v.getContext()); minSwipeDistance = config.getScaledTouchSlop(); } switch(event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); return true; case MotionEvent.ACTION_UP: upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > minSwipeDistance) { // left or right if (deltaX < 0) { if (listener != null) { listener.onRightSwipe(v); return true; } } if (deltaX > 0) { if (listener != null) { listener.onLeftSwipe(v); return true; } } } // swipe vertical? if(Math.abs(deltaY) > minSwipeDistance) { // top or down if (deltaY < 0) { if (listener != null) { listener.onDownSwipe(v); return true; } } if (deltaY > 0) { if (listener != null) { listener.onUpSwipe(v); return true; } } } } return false; } /** * Provides callbacks to a registered listener for swipe events in {@link SwipeDetector} * @author Phil Brown */ public interface SwipeListener { /** Callback for registering a new swipe motion from the bottom of the view toward its top. */ public void onUpSwipe(View v); /** Callback for registering a new swipe motion from the left of the view toward its right. */ public void onRightSwipe(View v); /** Callback for registering a new swipe motion from the right of the view toward its left. */ public void onLeftSwipe(View v); /** Callback for registering a new swipe motion from the top of the view toward its bottom. */ public void onDownSwipe(View v); } }
滑动拦截器视图
package self.philbrown; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.RelativeLayout; import com.npeinc.module_NPECore.model.SwipeDetector; import com.npeinc.module_NPECore.model.SwipeDetector.SwipeListener; /** * View subclass used for handling all touches (swipes and others) * @author Phil Brown */ public class SwipeInterceptorView extends RelativeLayout { private SwipeDetector swiper = null; public void setSwipeListener(SwipeListener listener) { if (swiper == null) swiper = new SwipeDetector(listener); } public SwipeInterceptorView(Context context) { super(context); } public SwipeInterceptorView(Context context, AttributeSet attrs) { super(context, attrs); } public SwipeInterceptorView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onTouchEvent(MotionEvent e) { boolean swipe = false, touch = false; if (swiper != null) swipe = swiper.onTouch(this, e); touch = super.onTouchEvent(e); return swipe || touch; } }
有一些关于Web(和这个页面)的使用ViewConfiguration的build议。 getScaledTouchSlop()具有SWIPE_MIN_DISTANCE的设备缩放值。
getScaledTouchSlop()
用于“ 滚动阈值”距离,而不是滑动。 滚动阈值距离必须小于“翻页”阈值距离。 例如,这个函数在我的Samsung GS2上返回12个像素,本页中引用的例子大约是100个像素。
通过API Level 8(Android 2.2,Froyo),你已经有getScaledPagingTouchSlop()
,用于页面滑动。 在我的设备上,它返回24(像素)。 所以,如果你在API级别<8,我认为“2 * getScaledTouchSlop()
”应该是“标准”滑动阈值。 但是,我的应用程序的小屏幕用户告诉我,这是太less了…在我的应用程序,你可以垂直滚动,水平改变页面。 有了build议的价值,他们有时会改变页面,而不是滚动。
我知道它为时已晚,但我仍然发布刷卡检测ListView ,如何在ListView项中使用滑动触摸监听器 。
参考:Exterminator13(本页中的答案之一)
创build一个ActivitySwipeDetector.class
package com.example.wocketapp; import android.content.Context; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "SwipeDetector"; private SwipeInterface activity; private float downX, downY; private long timeDown; private final float MIN_DISTANCE; private final int VELOCITY; private final float MAX_OFF_PATH; public ActivitySwipeDetector(Context context, SwipeInterface activity) { this.activity = activity; final ViewConfiguration vc = ViewConfiguration.get(context); DisplayMetrics dm = context.getResources().getDisplayMetrics(); MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density; VELOCITY = vc.getScaledMinimumFlingVelocity(); MAX_OFF_PATH = MIN_DISTANCE * 2; } public void onRightToLeftSwipe(View v) { Log.i(logTag, "RightToLeftSwipe!"); activity.onRightToLeft(v); } public void onLeftToRightSwipe(View v) { Log.i(logTag, "LeftToRightSwipe!"); activity.onLeftToRight(v); } public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { Log.d("onTouch", "ACTION_DOWN"); timeDown = System.currentTimeMillis(); downX = event.getX(); downY = event.getY(); v.getParent().requestDisallowInterceptTouchEvent(false); return true; } case MotionEvent.ACTION_MOVE: { float y_up = event.getY(); float deltaY = y_up - downY; float absDeltaYMove = Math.abs(deltaY); if (absDeltaYMove > 60) { v.getParent().requestDisallowInterceptTouchEvent(false); } else { v.getParent().requestDisallowInterceptTouchEvent(true); } } break; case MotionEvent.ACTION_UP: { Log.d("onTouch", "ACTION_UP"); long timeUp = System.currentTimeMillis(); float upX = event.getX(); float upY = event.getY(); float deltaX = downX - upX; float absDeltaX = Math.abs(deltaX); float deltaY = downY - upY; float absDeltaY = Math.abs(deltaY); long time = timeUp - timeDown; if (absDeltaY > MAX_OFF_PATH) { Log.e(logTag, String.format( "absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH)); return v.performClick(); } final long M_SEC = 1000; if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) { v.getParent().requestDisallowInterceptTouchEvent(true); if (deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if (deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, String.format( "absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE))); Log.i(logTag, String.format( "absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC))); } v.getParent().requestDisallowInterceptTouchEvent(false); } } return false; } public interface SwipeInterface { public void onLeftToRight(View v); public void onRightToLeft(View v); } }
Call it from your activity class like this:
yourLayout.setOnTouchListener(new ActivitySwipeDetector(this, your_activity.this));
And Don't forget to implement SwipeInterface which will give you two @override methods:
@Override public void onLeftToRight(View v) { Log.e("TAG", "L to R"); } @Override public void onRightToLeft(View v) { Log.e("TAG", "R to L"); }
To all: don't forget about case MotionEvent.ACTION_CANCEL:
it calls in 30% swipes without ACTION_UP
and its equal to ACTION_UP in this case
Gestures are those subtle motions to trigger interactions between the touch screen and the user. It lasts for the time between the first touch on the screen to the point when the last finger leaves the surface.
Android provides us with a class called GestureDetector using which we can detect common gestures like tapping down and up, swiping vertically and horizontally (fling), long and short press, double taps, etc . and attach listeners to them.
Make our Activity class implement GestureDetector.OnDoubleTapListener (for double tap gesture detection) and GestureDetector.OnGestureListener interfaces and implement all the abstract methods.For more info. you may visit https://developer.android.com/training/gestures/detector.html . Courtesy
For Demo Test. GestureDetectorDemo
If you dont like to create a separate class or make code complex,
You can just create a GestureDetector variable inside OnTouchListener and make your code more easier
namVyuVar can be any name of the View on which you need to set the listner
namVyuVar.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent MsnEvtPsgVal) { flingActionVar.onTouchEvent(MsnEvtPsgVal); return true; } GestureDetector flingActionVar = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener() { private static final int flingActionMinDstVac = 120; private static final int flingActionMinSpdVac = 200; @Override public boolean onFling(MotionEvent fstMsnEvtPsgVal, MotionEvent lstMsnEvtPsgVal, float flingActionXcoSpdPsgVal, float flingActionYcoSpdPsgVal) { if(fstMsnEvtPsgVal.getX() - lstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Right to Left fling return false; } else if (lstMsnEvtPsgVal.getX() - fstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Left to Right fling return false; } if(fstMsnEvtPsgVal.getY() - lstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Bottom to Top fling return false; } else if (lstMsnEvtPsgVal.getY() - fstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Top to Bottom fling return false; } return false; } }); });
I nedded a more generic Class , I took Tomas's class and added an Interface that send events to your Activity or Fragment. it will register the listener on the constructor so be sure you implement the interface or an ClassCastException will be thorwn. the interface returns one of the four final int defined in the class and will return the view on which it was activated upon.
import android.app.Activity; import android.support.v4.app.Fragment; import android.util.Log; import android.view.MotionEvent; import android.view.View; public class SwipeDetector implements View.OnTouchListener{ static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public final static int RIGHT_TO_LEFT=1; public final static int LEFT_TO_RIGHT=2; public final static int TOP_TO_BOTTOM=3; public final static int BOTTOM_TO_TOP=4; private View v; private onSwipeEvent swipeEventListener; public SwipeDetector(Activity activity,View v){ try{ swipeEventListener=(onSwipeEvent)activity; } catch(ClassCastException e) { Log.e("ClassCastException",activity.toString()+" must implement SwipeDetector.onSwipeEvent"); } this.v=v; } public SwipeDetector(Fragment fragment,View v){ try{ swipeEventListener=(onSwipeEvent)fragment; } catch(ClassCastException e) { Log.e("ClassCastException",fragment.toString()+" must implement SwipeDetector.onSwipeEvent"); } this.v=v; } public void onRightToLeftSwipe(){ swipeEventListener.SwipeEventDetected(v,RIGHT_TO_LEFT); } public void onLeftToRightSwipe(){ swipeEventListener.SwipeEventDetected(v,LEFT_TO_RIGHT); } public void onTopToBottomSwipe(){ swipeEventListener.SwipeEventDetected(v,TOP_TO_BOTTOM); } public void onBottomToTopSwipe(){ swipeEventListener.SwipeEventDetected(v,BOTTOM_TO_TOP); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; //HORIZONTAL SCROLL if(Math.abs(deltaX) > Math.abs(deltaY)) { if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX < 0) { this.onLeftToRightSwipe(); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(); return true; } } else { //not long enough swipe... return false; } } //VERTICAL SCROLL else { if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onTopToBottomSwipe(); return true; } if(deltaY > 0) { this.onBottomToTopSwipe(); return true; } } else { //not long enough swipe... return false; } } return true; } } return false; } public interface onSwipeEvent { public void SwipeEventDetected(View v , int SwipeType); } }