前言

Android提供了一个GestureDetector来帮助我们识别一些基本的触摸手势(还有ScaleGestureDetector可以识别缩放手势),让我们很方便地实现手势控制功能

1 GestureDetector

GestureDetector 可以使用 MotionEvents 检测各种手势和事件。GestureDetector.OnGestureListener 是一个回调方法,在发生特定的事件时会调用 Listener 中对应的方法回调。这个类只能用于检测触摸事件的 MotionEvent,不能用于轨迹球事件。

常用方法

  • isLongpressEnabled (),是否允许长点击
  • onTouchEvent(MotionEvent ev) ,拦截事件进行处理。比如在哪个Activity设置手势识别,那么就需要重写该Activity的onTouchEvent。将事件处理转交给GestureDetetor。
  • setIsLongpressEnabled(boolean isLongpressEnabled) ,设置是否允许长点击。
  • setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) ,设置双点击事件回调监听。

GestureDetector.OnGestureListener

  • OnDown(MotionEvent e):用户按下屏幕就会触发;
  • onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) ,滑屏,用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发 参数解释: e1:第1个ACTION_DOWN MotionEvent e2:最后一个ACTION_MOVE MotionEvent velocityX:X轴上的移动速度,像素/秒 velocityY:Y轴上的移动速度,像素/秒。
  • onLongPress(MotionEvent e) ,长按触摸屏,超过一定时长,就会触发这个事件 触发顺序: onDown->onShowPress->onLongPress。
  • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) ,在屏幕上拖动事件。无论是用手拖动view,或者是以抛的动作滚动,都会多次触发,这个方法 在ACTION_MOVE动作发生时就会触发 滑屏:手指触动屏幕后,稍微滑动后立即松开 onDown—–》onScroll—-》onScroll—-》onScroll—-》………—–>onFling 拖动 onDown——》onShowPress —》onScroll—-》onScroll——》onFiling 可见,无论是滑屏,还是拖动,影响的只是中间OnScroll触发的数量多少而已,最终都会触发onFling事件!
  • onShowPress(MotionEvent e) ,如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行。这个时间是底层封装,设定的。开发者在应用层只需应用,不必知晓时间设定以及原理。
  • onSingleTapUp(MotionEvent e) ,顾名思义,一次单独的轻击抬起操作,也就是轻击一下屏幕,立刻抬起来,才会有这个触发,当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以也就不会触发这个事件 触发顺序: 点击一下非常快的(不滑动)Touchup: onDown->onSingleTapUp->onSingleTapConfirmed 点击一下稍微慢点的(不滑动)Touchup: onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed。

使用

public class FiveActivity extends AppCompatActivity {    private GestureDetector gestureDetector;    private GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener() {        // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发        @Override        public boolean onDown(MotionEvent e) {            Log.e("MainActivity","onDown");            return false;        }        /*         * 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发         * 注意和onDown()的区别,强调的是没有松开或者拖动的状态         *         * 而onDown也是由一个MotionEventACTION_DOWN触发的,但是他没有任何限制,         * 也就是说当用户点击的时候,首先MotionEventACTION_DOWN,onDown就会执行,         * 如果在按下的瞬间没有松开或者是拖动的时候onShowPress就会执行,如果是按下的时间超过瞬间         * (这块我也不太清楚瞬间的时间差是多少,一般情况下都会执行onShowPress),拖动了,就不执行onShowPress。         */        @Override        public void onShowPress(MotionEvent e) {            Log.e("MainActivity","onShowPress");        }        // 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发        // 轻击一下屏幕,立刻抬起来,才会有这个触发        // 从名子也可以看出,一次单独的轻击抬起操作,当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以这个事件 就不再响应        @Override        public boolean onSingleTapUp(MotionEvent e) {            Log.e("MainActivity","onSingleTapUp");            return false;        }        // 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            Log.e("MainActivity","onScroll");            return false;        }        // 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发        @Override        public void onLongPress(MotionEvent e) {            Log.e("MainActivity","onLongPress");        }        // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发        @Override        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {            Log.e("MainActivity","onFling");            return false;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_five);        ButterKnife.bind(this);        gestureDetector = new GestureDetector(this,listener);        gestureDetector.isLongpressEnabled();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return gestureDetector.onTouchEvent(event);    }}

GestureDetector.OnDoubleTapListener

  • onSingleTapConfirmed(MotionEvent e):单 击事件。用来判定该次点击是SingleTap而不是DoubleTap,如果连续点击两次就是DoubleTap手势,如果只点击一次,系统等待一段时 间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,然后触发 SingleTapConfirmed事件。触发顺序 是:OnDown->OnsingleTapUp->OnsingleTapConfirmed ,关于 onSingleTapConfirmed和onSingleTapUp的一点区别: OnGestureListener有这样的一个方法onSingleTapUp,和onSingleTapConfirmed容易混淆。二者的区别 是:onSingleTapUp,只要手抬起就会执行,而对于onSingleTapConfirmed来说,如果双击的话,则 onSingleTapConfirmed不会执行。
  • onDoubleTap(MotionEvent e):双击事件
  • onDoubleTapEvent(MotionEvent e):双击间隔中发生的动作。指触发onDoubleTap以后,在双击之间发生的其它动作,包含down、up和move事件。

GestureDetector.SimpleOnGestureListener

1、这是一个类,在它基础上新建类的话,要用extends派生而不是用implements继承!
2、 OnGestureListener和OnDoubleTapListener接口里的函数都是强制必须重写的,即使用不到也要重写出来一个空函数但在 SimpleOnGestureListener类的实例或派生类中不必如此,可以根据情况,用到哪个函数就重写哪个函数,因为 SimpleOnGestureListener类本身已经实现了这两个接口的所有函数,只是里面全是空的而已。
实例

public class FiveActivity extends AppCompatActivity {    @BindView(R.id.image_gestrue)    ImageView imageGestrue;    @BindView(R.id.my_geture_test_1)    LinearLayout myGetureTest1;    @BindView(R.id.small_image1)    ImageView smallImage1;    @BindView(R.id.small_image2)    ImageView smallImage2;    @BindView(R.id.small_image3)    ImageView smallImage3;    @BindView(R.id.small_image4)    ImageView smallImage4;    @BindView(R.id.small_image5)    ImageView smallImage5;    private GestureDetector gestureDetector;    private ImageView[] imgView;    /**图片下标计数器*/    private int count=0;    /**图片资源数组*/    private int [] imageId;    private GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener() {        // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发        @Override        public boolean onDown(MotionEvent e) {            Log.e("MainActivity", "onDown");            return false;        }        /*         * 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发         * 注意和onDown()的区别,强调的是没有松开或者拖动的状态         *         * 而onDown也是由一个MotionEventACTION_DOWN触发的,但是他没有任何限制,         * 也就是说当用户点击的时候,首先MotionEventACTION_DOWN,onDown就会执行,         * 如果在按下的瞬间没有松开或者是拖动的时候onShowPress就会执行,如果是按下的时间超过瞬间         * (这块我也不太清楚瞬间的时间差是多少,一般情况下都会执行onShowPress),拖动了,就不执行onShowPress。         */        @Override        public void onShowPress(MotionEvent e) {            Log.e("MainActivity", "onShowPress");        }        // 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发        // 轻击一下屏幕,立刻抬起来,才会有这个触发        // 从名子也可以看出,一次单独的轻击抬起操作,当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以这个事件 就不再响应        @Override        public boolean onSingleTapUp(MotionEvent e) {            Log.e("MainActivity", "onSingleTapUp");            return false;        }        // 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发        @Override        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            Log.e("MainActivity", "onScroll");            return false;        }        // 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发        @Override        public void onLongPress(MotionEvent e) {            Log.e("MainActivity", "onLongPress");        }        // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发        @Override        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {            Log.e("MainActivity", "onFling");            //从右往左移动            if(e1.getX()-e2.getX()>50){                //下标++                count++;                if(count<=4){                    //切换到下一张                    imageGestrue.setImageResource(imageId[count]);                    imgView[count].setImageResource(R.mipmap.pic7);                    imgView[count-1].setImageResource(R.mipmap.ic_launcher);                }else{                    //滑动到最后一张,该方向不会再滑动,就停留在最后一张                    count=4;                    imageGestrue.setImageResource(imageId[count]);                }            }            //从左往右移动            if(e1.getX()-e2.getX()<50){                count--;                if(count>=0){                    imageGestrue.setImageResource(imageId[count]);                    imgView[count].setImageResource(R.mipmap.pic7);                    imgView[count+1].setImageResource(R.mipmap.ic_launcher);                }else{                    count=0;                    imageGestrue.setImageResource(imageId[count]);                }            }            return false;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_five);        ButterKnife.bind(this);        //得到图片资源的Id        imageId = new int[]{                R.mipmap.pic1,                R.mipmap.pic2,                R.mipmap.pic3,                R.mipmap.pic4,                R.mipmap.pic5};        //得到图片视图 ImageView        imgView = new ImageView []{                smallImage1,                smallImage2,                smallImage3,                smallImage4,                smallImage5};        gestureDetector = new GestureDetector(this, listener);        gestureDetector.isLongpressEnabled();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return gestureDetector.onTouchEvent(event);    }}
效果图.gif

2 缩放手势检测(ScaleGestureDetector)

缩放手势检测同样是官方提供的手势检测工具,它的使用方式的 GentureDetector 类似,也是通过 Listener 进行监听用户的操作手势,它是对缩放手势进行了一次封装, 可以方便用户快速的完成缩放相关功能的开发。缩放手势相对比较简单,网络上也能查到不少非官方实现的缩放手势计算方案,但部分非官方的方案确实有所局限,例如只支持两个手指的计算,在出现超过两个手指时,只计算了前两个手指的移动,这样显然是不合理的。而官方的这种实现方案轻松的应对了多个手指的情况,下面我们就来看看它是如何实现的吧。

ScaleGestureDetector

  • float getScaleFactor()
    返回从之前的缩放手势和当前的缩放手势两者的比例因子,即缩放值,默认1.0。当手指做缩小操作时,该值小于1.0,当手指做放大操作时,该值大于1.0。在每一个缩放事件中都会从1.0开始变化,如果需要做持续性操作,则需要保存上一次的伸缩值,然后当前的伸缩值上一次的伸缩值,得到连续变化的伸缩值,例如有3次事件发生,没连续性则是缩小0.9->缩小0.9->缩小0.9,如果做了连续性处理即保存上一次的伸缩值,则是缩小0.9->缩小0.90.9=0.81->缩小0.81*0.9=0.72,有了逐渐变小的连续性。
  • float getFocusX()
    返回当前缩放手势的焦点X坐标,焦点即两手指的中心点。
  • float getFocusY()
    返回当前缩放手势的焦点Y坐标,即两手指的中心点。
  • float getCurrentSpan()
    返回每个缩放手势的两个手指之间的距离值。
监听器 简介
OnScaleGestureListener 缩放手势检测器。
SimpleOnScaleGestureListener 缩放手势检测器的空实现。

OnScaleGestureListener

方法 简介
boolean onScaleBegin(ScaleGestureDetectordetector) 缩放手势开始,当两个手指放在屏幕上的时候会调用该方法(只调用一次)。如果返回 false 则表示不使用当前这次缩放手势。
boolean onScale(ScaleGestureDetectordetector) 缩放被触发(会调用0次或者多次),如果返回 true 则表示当前缩放事件已经被处理,检测器会重新积累缩放因子,返回 false 则会继续积累缩放因子。
void onScaleEnd(ScaleGestureDetectordetector) 缩放手势结束。

更多相关文章

  1. Android(安卓)ImageView.ScaleType详解
  2. android 常用Bitmap处理方法收集:普通裁剪,缩放,圆形裁剪
  3. Android(安卓)调用相册 拍照 实现系统控件缩放 切割图片
  4. Android自定义UI手势密码改进版源码下载
  5. Android(安卓)内存溢出问题分析。
  6. android手势事件 快速移动 长按触摸屏 按下触摸屏,并拖动
  7. Android之ViewFlipper的用法
  8. android应用中图片的裁剪
  9. 记录Android中使用ViewDragHelper实现拖动View滑动的方法

随机推荐

  1. 如何让Android模拟器的Browser可以浏览In
  2. Android开源项目汇总
  3. 在Android下抓包
  4. Android(安卓)Studio 检测内存泄漏与解决
  5. 2012年总结
  6. 在cmd窗口中查询android的sqlite3数据库
  7. Android中Fragment嵌套Fragment,切换Fragm
  8. 【Android(安卓)Developers Training】 7
  9. Android内存溢出的解决方法(VMRuntime.get
  10. Android四种模式里隐含的哲学