首页 文章

网格布局上的手势检测

提问于
浏览
1024

我想在我的Android应用程序中使用 fling 手势检测功能 .

我所拥有的是包含9 ImageViewGridLayout . 来源可以在这里找到:Romain Guys's Grid Layout .

我拿的那个文件来自Romain Guy的Photostream application,并且只是略微适应了 .

对于简单的点击情况,我只需要为每个 ImageView 设置 onClickListener 我添加为实现 View.OnClickListener 的主 activity . 实现识别 fling 的东西似乎无比复杂 . 我认为这是因为它可能跨越 views

  • 如果我的活动实现 OnGestureListener 我不知道如何将其设置为我添加的 GridImage 视图的手势监听器 .
public class SelectFilterActivity extends Activity implements
   View.OnClickListener, OnGestureListener { ...
  • 如果我的活动实现了 OnTouchListener 那么我没有 onFling 方法 override (它有两个事件作为参数,允许我确定投掷是否值得注意) .
public class SelectFilterActivity extends Activity implements
    View.OnClickListener, OnTouchListener { ...
  • 如果我创建自定义 View ,如 GestureImageView ,扩展 ImageView 我不知道如何告诉活动从视图中发生了 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;
        }
    }
});

是否可以在屏幕顶部放置透明视图来捕捉啪啪声?

如果我从XML中选择不使用 inflate 我的子图像视图,我可以将 GestureDetector 作为构造函数参数传递给我创建的 ImageView 的新子类吗?

这是一个非常简单的活动,我试图让 fling 检测工作:SelectFilterActivity (Adapted from photostream) .

我一直在看这些来源:

到目前为止,没有什么对我有用,我希望得到一些指示 .

18 回答

  • 788

    我 Build 了一个更通用的类,我使用了Tomas的类,并添加了一个将事件发送到Activity或Fragment的接口 . 它将在构造函数上注册侦听器,因此请确保实现接口或者ClassCastException将是thorwn . 接口返回类中定义的四个final int之一,并返回激活它的视图 .

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

    如果您不想创建单独的类或使代码复杂,
    您可以在OnTouchListener中创建一个GestureDetector变量,使代码更容易

    namVyuVar可以是您需要设置列表器的View的任何名称

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

    上面的答案之一提到处理不同的像素密度,但建议手动计算滑动参数 . 值得注意的是,您实际上可以使用 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)
    

    我注意到使用这些值会导致应用程序和系统其余部分之间的“感觉”更加一致 .

  • 13

    上面的滑动手势检测器代码非常有用!但是,您可能希望通过使用以下相对值 (REL_SWIPE) 而不是绝对值 (SWIPE_) 来使此解决方案密度不可知 .

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

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

    当你的被覆盖的方法被击中时,请注意,活动的 onClick(View v) 和手势监听器的 onFling .

    public void onClick(View v) {
      Filter f = (Filter) v.getTag();
      FilterFullscreenActivity.show(this, input, f);
    }
    

    帖子'fling'舞蹈是可选的,但鼓励 .

  • 14

    也作为一个小的增强 .

    try / catch块的主要原因是e1对于初始移动可能为null . 除了try / catch之外,还包括null和return的测试 . 类似如下

    if (e1 == null || e2 == null) return false;
    try {
    ...
    } catch (Exception e) {}
    return false;
    
  • 7

    您可以使用droidQuery库来处理flings,click,long clicks和自定义事件 . 该实现基于我之前的答案,但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 来显示如何覆盖 ViewonTouch 方法以允许滑动事件和其他检测到的事件(例如点击或长按) .

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

    Swipe Interceptor View

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

    Thomas FankhauserMarek 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;
        }
    
    }
    
  • 12

    这里有很多很棒的信息 . 不幸的是,很多这种处理代码分散在各种完成状态的各个站点上,尽管人们会认为这对许多应用程序来说都是必不可少的 .

    我花时间创建了一个fling listener来验证是否符合相应的条件 . 我添加了一个page fling listener,它增加了更多检查以确保晃动达到翻页的门槛 . 这两种侦听器都允许您轻松地将晃动限制在水平轴或垂直轴上 . 您可以在view for sliding images中看到它是如何使用的 . 我承认这里的人已完成了大部分的研究 - 我只是把它放在一个可用的库中 .

    最近几天代表我第一次尝试在Android上编码;期待much more来 .

  • 2

    这个问题有点陈旧,2011年7月谷歌发布了Compatibility Package, revision 3),其中包括适用于Android 1.6的 ViewPager . 发布此问题的 GestureListener 答案正在寻找用于在Android画廊中切换照片或在新的Play Market应用中切换视图时使用的代码,然后肯定是 ViewPager .

    Here's some links for more info:

  • 144

    对所有人:不要忘记 case MotionEvent.ACTION_CANCEL:

    它在没有ACTION_UP的情况下调用30%的滑动

    在这种情况下它等于ACTION_UP

  • 11

    我做的有点不同,并编写了一个额外的检测器类来实现 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;
    }
    
    }
    

    对我来说真的很好用!

  • 4

    我从 Thomas Fankhauser 略微修改并修复了解决方案

    整个系统由两个文件组成, SwipeInterfaceActivitySwipeDetector


    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事件的View .

    @Override
    public void left2right(View v) {
        switch(v.getId()){
            case R.id.swipe_layout:
                // do your stuff here
            break;
        }       
    }
    
  • 206

    我知道为时已晚,无法回答但仍然发布 Swipe Detection for ListView 如何在ListView项目中使用Swipe Touch Listener .

    参考:Exterminator13(本页答案之一)

    制作一个 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));
    

    并且不要忘记实现 SwipeInterface ,它将为您提供两个@override方法:

    @Override
        public void onLeftToRight(View v) 
        {
            Log.e("TAG", "L to R");
        }
    
        @Override
        public void onRightToLeft(View v) 
        {
            Log.e("TAG", "R to L");
        }
    
  • 12

    手势是触发触摸屏和用户之间交互的微妙动作 . 它持续从屏幕上的第一次触摸到最后一根手指离开表面之间的时间 .

    Android为我们提供了一个名为GestureDetector的类,使用它我们可以检测常见的手势,如向下和向上,垂直和水平滑动(fling),长按和双按,双击等,并将听众附加到它们 .

    制作我们的活动class implement GestureDetector.OnDoubleTapListener(用于双击手势检测)和GestureDetector.OnGestureListener interfaces并实现所有抽象方法 . 有关详细信息 . 你可以访问https://developer.android.com/training/gestures/detector.html . Courtesy

    用于演示测试 . GestureDetectorDemo

  • 4

    如果有人想要一个有效的实现,这是顶部两个答案的综合答案 .

    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() {}
    }
    
  • 18

    有一个内置界面,您可以直接使用所有手势:
    以下是基本级别用户的解释:
    enter image description here
    有两个进口要小心选择两者都不同
    enter image description here

    enter image description here

  • 64

    在Web(和本页)上有一些使用ViewConfiguration的命题 . getScaledTouchSlop() 具有 SWIPE_MIN_DISTANCE 的设备缩放值 .

    getScaledTouchSlop() 用于“ scrolling 阈值" distance, not swipe. The scrolling threshold distance has to be smaller than a "页面间摆动”阈值距离 . 例如,此功能在我的Samsung GS2上返回12个像素,此页面中引用的示例大约为100像素 .

    使用API Level 8(Android 2.2,Froyo),您有 getScaledPagingTouchSlop() ,用于页面滑动 . 在我的设备上,它返回24(像素) . 因此,如果您的API级别<8,我认为“2 * getScaledTouchSlop() " should be the " standard”滑动阈值 . 但我的小屏幕应用程序的用户告诉我它太少了......在我的应用程序中,您可以垂直滚动,并水平更改页面 . 使用建议的值,它们有时会更改页面而不是滚动 .

相关问题