手势识别 GestureDetector ScaleGestureDetector

识别器GestureDetector基本介绍


当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等。一般情况下,我们可以通过View或Activity的onTouchEvent,或实现OnTouchListener接口后通过onTouch方法,处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹去判断是什么手势)。为此,Android 给我们提供了GestureDetector类(Gesture:手势,Detector:识别),通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent方法完成不同手势的识别。

虽然他能识别手势,但是不同的手势要怎么处理,还是给程序员自己要实现的。

注意: 默认情况下,GestureDetector是监听不到MotionEvent事件的,也即GestureDetector的onTouchEvent方法是不会被调用的,若要被调用,必须满足两个条件
  • ①在View的onTouchEvent()方法中,须将MotionEvent事件手动【传】给GestureDetector
  • ②若View设置了.setOnTouchListener()监听器,则其onTouch()的返回值还须是【false】,或者在onTouch()中将MotionEvent事件手动【传】给GestureDetector
只有这样,GestureDetector 注册的OnGestureListener(单击)或OnDoubleTapListener(双击)才能获得完整的MotionEvent事件,进而根据该对象封装的的信息,做出合适的反馈。

识别器ScaleGestureDetector基本介绍


ScaleGestureDetector是Android2.2 新增的类,其能使 Views 通过接收到的 MotionEvents ,检测包括多点触摸在内的手势变化信息,当检测到此类信息后,监听器OnScaleGestureListener 便会调用相应的回调方法通知用户,其最简单常用的应用场景就是使用双指缩放图片。

注意,ScaleGestureDetector是直接继承自object的,并非GestureDetector的子类。

监听器OnScaleGestureListener回调方法有onScale(ScaleGestureDetector) 、onScaleBegin、onScaleEnd

ScaleGestureDetector中定义的公共方法:
  • public float getCurrentSpan () 返回手势过程中,组成该手势的两个触点的当前距离。
  • public long getEventTime () 返回事件被捕捉时的时间。
  • public float getFocusX () 返回当前手势焦点的 X 坐标。 如果手势正在进行中,焦点位于组成手势的两个触点之间。 如果手势正在结束,焦点为仍留在屏幕上的触点的位置。若 isInProgress() 返回 false,该方法的返回值未定义。
  • public float getFocusY ()  返回当前手势焦点的 Y 坐标。
  • public float getPreviousSpan () 返回手势过程中,组成该手势的两个触点的前一次距离。
  • public float getScaleFactor () 返回从前一个伸缩事件至当前伸缩事件的伸缩比率。该值定义为  getCurrentSpan() / getPreviousSpan()。
  • public long getTimeDelta () 返回前一次接收到的伸缩事件距当前伸缩事件的时间差,以毫秒为单位。
  • public boolean isInProgress () 如果手势处于进行过程中,返回 true。否则返回 false。

手势监听器SimpleOnGestureListeren


我们只需要继承SimpleOnGestureListener重载自己需要监听的手势即可

public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {}

OnGestureListener的监听事件
  • 按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下
  • 抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作,onDown---onScroll---onScroll---…onFling
  • 长按(onLongPress): 手指按在持续一段时间,并且没有松开
  • 滚动(onScroll): 手指在触摸屏上滑动,onDown---onScroll---onScroll---…onScroll
  • 按住(onShowPress): 手指按在触摸屏上,在按下起效,在长按前失效,onDown->onShowPress->onLongPress
  • 抬起(onSingleTapUp):手指离开触摸屏的那一刹那

OnDoubleTapListener的监听事件
  • onDoubleTap,双击的【第二下】down时触发(只执行一次)
  • onDoubleTapEvent,双击的【第二下】down和up都会触发(执行次数不确定)
  • onSingleTapConfirmed单击确认,即很快的按下并抬起,但并不连续点击第二下

注意:
  • onSingleTapConfirmed(单击)和onSingleTapUp都是在down后既没有滑动onScroll,又没有长按onLongPress时, up 时触发的
  • 非常快的点击一下:onDown->onSingleTapUp->onSingleTapConfirmed
  • 稍微慢点的点击一下:onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed(最后一个不一定会触发)

演示代码-在Activity中使用

上面的图片是添加了GestureDetector和ScaleGestureDetector的ImageView,并且在【onTouchEvent】方法中将事件传递给手势识别器,且返回【true】下面的图片是在上面图片基础上注册了【OnTouchListener】,若在其【onTouch】方法中返回【true】,则就会导致其注册的手势识别器全部失效!我们为Activity自身注册了GestureDetector和ScaleGestureDetector,并且在其【onTouchEvent】方法中将事件传递给手势识别器,返回值使用默认逻辑值

public class MainActivity extends Activity {

    private GestureDetector mGestureDetector;//用于Activity自身的单击和双击事件手势识别器

    private ScaleGestureDetector mScaleGestureDetector;//用于Activity自身的缩放事件手势识别器

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        MyImageView myImageView = (MyImageView) findViewById(R.id.iv);

        myImageView.setOnTouchListener(new OnTouchListener() {

            @Override

            public boolean onTouch(View v, MotionEvent event) {

                return true;//返回值要为false,或者直接在此onTouch()中将MotionEvent事件手动传给GestureDetector,才能使手势识别器生效

            }

        });

        //**************************************************************************************************************************

        mGestureDetector = new GestureDetector(this, new SimpleOnGestureListener() {

            @Override

            //监测双击事件

            public boolean onDoubleTap(MotionEvent e) {

                Log.e("Activity-手势", "onDoubleTap");

                return super.onDoubleTap(e);

            }

        });

        mScaleGestureDetector = new ScaleGestureDetector(this, new SimpleOnScaleGestureListener() {

            @Override

            //监测缩放

            public boolean onScale(ScaleGestureDetector detector) {

                DecimalFormat df = new DecimalFormat("0.00");//格式化float

                Log.e("Activity-缩放", "onScale," + df.format(detector.getScaleFactor()) + "-" + //缩放因子,两指靠拢时小于1

                        df.format(detector.getFocusX()) + "-" + df.format(detector.getFocusY()));//中心点坐标

                return super.onScale(detector);

            }

        });

    }

    //**************************************************************************************************************************

    @Override

    //重写屏幕触摸事件onTouchEvent,将触摸事件传给GestureDetector

    public boolean onTouchEvent(MotionEvent event) {

        try {

            //在onTouchEvent方法中可以完成所有GestureDetector的操作,因为他们都只需要一个MotionEvent的参数

            Log.e("Activity-多点触摸", "onTouchEvent" + spacing(event));

        } catch (Exception e) {

        }

        mGestureDetector.onTouchEvent(event);

        mScaleGestureDetector.onTouchEvent(event);

        return super.onTouchEvent(event);

    }

    /**

     * 返回两个点之间的距离

     */

    private float spacing(MotionEvent event) {

        float x = event.getX(0) - event.getX(1);

        float y = event.getY(0) - event.getY(1);

        return FloatMath.sqrt(x * x + y * y);

    }

}

演示代码-在View中使用


public class MyImageView extends ImageView {

    private GestureDetector mGestureDetector;//单击和双击事件手势识别

    private ScaleGestureDetector mScaleGestureDetector;//缩放事件手势识别

    public MyImageView(Context context) {

        this(context, null);

    }

    public MyImageView(Context context, AttributeSet attr) {

        super(context, attr);

        mGestureDetector = new GestureDetector(context, new MyGestureListener());

        mScaleGestureDetector = new ScaleGestureDetector(context, new MyScaleGestureListener());

    }

    @Override

    public boolean onTouchEvent(MotionEvent event) {

        mGestureDetector.onTouchEvent(event);

        mScaleGestureDetector.onTouchEvent(event);

        //return super.onTouchEvent(event);//不管返回值是什么,都能接收down事件,都能触发onDown、onShowPress、onLongPress

        return true;//但只有返回true才能继续接收move,up等事件,也才能响应ScaleGestureDetector事件及GestureDetector中与move,up相关的事件

    }

    //**************************************************************************************************************************

    private class MyScaleGestureListener extends SimpleOnScaleGestureListener {

        @Override

        public boolean onScale(ScaleGestureDetector detector) {

            Log.e("view-缩放", "onScale," + detector.getScaleFactor());

            return super.onScale(detector);

        }

        @Override

        public boolean onScaleBegin(ScaleGestureDetector detector) {

            Log.e("view-缩放", "onScaleBegin");

            return super.onScaleBegin(detector);

        }

        @Override

        public void onScaleEnd(ScaleGestureDetector detector) {

            Log.e("view-缩放", "onScaleEnd");

        }

    }

    //**************************************************************************************************************************

    private class MyGestureListener extends SimpleOnGestureListener {

        @Override

        //双击的【第二下】Touch down时触发(只执行一次)

        public boolean onDoubleTap(MotionEvent e) {

            Log.e("view-手势", "onDoubleTap");

            return super.onDoubleTap(e);

        }

        @Override

        //双击的【第二下】Touch down和up都会触发(执行次数不确定)。 

        public boolean onDoubleTapEvent(MotionEvent e) {

            Log.e("view-手势", "onDoubleTapEvent");

            return super.onDoubleTapEvent(e);

        }

        @Override

        //Touch down时触发

        public boolean onDown(MotionEvent e) {

            Log.e("view-手势", "onDown");

            return super.onDown(e);

        }

        @Override

        //onScroll一点距离后,【抛掷时】触发(若是轻轻的、慢慢的停止活动,而非抛掷,则很可能不触发)

        //参数为手指接触屏幕、离开屏幕一瞬间的动作事件,及手指水平、垂直方向移动的速度,像素/秒

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            Log.e("view-手势", "onFling");

            if ((e2.getRawX() - e1.getRawX()) > 100) {

                Log.e("view-手势", "onFling-从左到右滑");

                return true;

            }

            return super.onFling(e1, e2, velocityX, velocityY);

        }

        @Override

        //Touch了不移动一直Touch down时触发 

        public void onLongPress(MotionEvent e) {

            Log.e("view-手势", "onLongPress");

            super.onLongPress(e);

        }

        @Override

        //Touch了滑动时触发,e1代表触摸时的事件,是不变的,e2代表滑动过程中的事件,是时刻变化的

        //distance是当前event2与上次回调时的event2之间的距离,代表上次回调之后到这次回调之前移动的距离

        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

            Log.e("view-手势", "onScroll-" + (int) e1.getX() + "-" + (int) e2.getX() + "-" + (int) distanceX + "-" + (int) distanceY);

            return super.onScroll(e1, e2, distanceX, distanceY);

        }

        @Override

        //Touch了还没有滑动时触发 

        public void onShowPress(MotionEvent e) {

            Log.e("view-手势", "onShowPress");

            super.onShowPress(e);

        }

        @Override

        //在touch down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touchup时触发。

        public boolean onSingleTapConfirmed(MotionEvent e) {

            Log.e("view-手势", "onSingleTapConfirmed");

            return super.onSingleTapConfirmed(e);

        }

        @Override

        //在touch down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touchup时触发。

        public boolean onSingleTapUp(MotionEvent e) {

            Log.e("view-手势", "onSingleTapUp");

            return super.onSingleTapUp(e);

        }

    }

}

布局



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="#1234"

    android:orientation="vertical" >

    <com.bqt.test.MyImageView

        android:layout_width="match_parent"

        android:layout_height="150dp"

        android:background="#10ff"

        android:src="@drawable/ic_launcher" />

    <com.bqt.test.MyImageView

        android:id="@+id/iv"

        android:layout_width="match_parent"

        android:layout_height="150dp"

        android:background="#1f00"

        android:src="@drawable/ic_launcher" />

</LinearLayout>

来自为知笔记(Wiz)

时间: 2024-11-13 08:14:31

手势识别 GestureDetector ScaleGestureDetector的相关文章

图片--Android有效解决加载大图片时内存溢出的问题

Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView

显示大图片的技巧

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsse

Android PhotoView基本功能实现

Android开发过程中,想必都使用过PhotoView来实现图片展示的功能.在最新版的sdk(android-23)有了一个原生的photoView,并且代码实现也很简单,逻辑也很清晰.我们在实际的工作中,遇到的需求可能与这些photoview现有功能有些细微的差别,需要修改,或者重新开发.本文简单介绍下android-23中photoview涉及到的相关技术,相信读者看完后会发现,其实很简单.以下为实现思路和步骤 1 自定义一个View 通过自定义视图,继承View,为外界提过public接

java.lang.OutOfMemoryError: bitmap size exceeds VM budget解决方法

1 BitmapFactory.decodeFile(imageFile); 用BitmapFactory解码一张图片时,有时会遇到该错误.这往往是由于图片过大造成的.要想正常使用,则需要分配更少的内存空间来存储. BitmapFactory.Options.inSampleSize 设置恰当的inSampleSize可以使BitmapFactory分配更少的空间以消除该错误.inSampleSize的具体含义请参考SDK文档.例如: 1 2 3 BitmapFactory.Options op

Android有效解决加载大图片时内存溢出的问题

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsse

Android加载大图片OOM异常解决

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source, decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsse

Android性能优化的一些方案

优化Dalvik虚拟机的堆内存分配 1)首先内存方面,可以参考 Android堆内存也可自己定义大小和优化Dalvik虚拟机的堆内存分配 对于Android平台来说,其托管层使用的Dalvik JavaVM从目前的表现来看还有很多地方可以优化处理,比如我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率.当然具体原理我们可以参考开源工程,这里我们

Android OOM的解决方式

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图. 由于这些函数在完毕decode后,终于都是通过java层的createBitmap来完毕的,须要消耗很多其它内存. 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source. decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAs

android 内存和性能优化汇总

1.即时编译(Just-in-time Compilation,JIT),又称动态转译(Dynamic Translation),是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术.即时编译前期的两个运行时理论是字节码编译和动态编译.Android原来Dalvik虚拟机是作为一种解释器实现,新版(Android2.2+)将换成JIT编译器实现.性能测试显示,在多项测试中新版本比旧版本提升了大约6倍. 2. 就像世界上没有免费的午餐,世界上也没有免费的对象.虽然gc为每个线程