Android基础知识之手势详解(图片缩放,位移,单击放大功能)

概述

Android 提供了基本的手势检测工具类,我们拿来就可以用的,主要有两个GestureDetector和ScaleGestureDetector类,如何使用,具体见下面的介绍。

1.GestureDetector使用

先看看这个类的结构,有三个接口组成,包含一个SimpleOnGestureListener类,而这个类是对三个接口的实现。
Android基础知识之手势详解(图片缩放,位移,单击放大功能)_第1张图片
通过自定义view并完成双击图片可以放大,再次双击还原,并实现随意拖动图片功能,介绍这两个手势工具类的使用情况。先看看GestureDetector.OnGestureListener的函数:

  • GestureDetector.OnGestureListener接口
public class GestureDetector {
    /**
     * The listener that is used to notify when gestures occur.
     * If you want to listen for all the different gestures then implement
     * this interface. If you only want to listen for a subset it might
     * be easier to extend {@link SimpleOnGestureListener}.
     */
    public interface OnGestureListener {

        /**
         * Notified when a tap occurs with the down {@link MotionEvent}
         * that triggered it. This will be triggered immediately for
         * every down event. All other events should be preceded by this.
         *
         * @param e The down motion event.
         */
        boolean onDown(MotionEvent e);//手指触摸到屏幕就会触发。

        /**
         * The user has performed a down {@link MotionEvent} and not performed
         * a move or up yet. This event is commonly used to provide visual
         * feedback to the user to let them know that their action has been
         * recognized i.e. highlight an element.
         *
         * @param e The down motion event
         */
        void onShowPress(MotionEvent e);//按着不放的过程,知道触发onLongPress,介于onDown与onLongPress之间。

        /**
         * Notified when a tap occurs with the up {@link MotionEvent}
         * that triggered it.
         *
         * @param e The up motion event that completed the first tap
         * @return true if the event is consumed, else false
         */
        boolean onSingleTapUp(MotionEvent e);//单击屏幕拿起时触发。

        /**
         * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
         * current move {@link MotionEvent}. The distance in x and y is also supplied for
         * convenience.
         *
         * @param e1 The first down motion event that started the scrolling.
         * @param e2 The move motion event that triggered the current onScroll.
         * @param distanceX The distance along the X axis that has been scrolled since the last
         *              call to onScroll. This is NOT the distance between {@code e1}
         *              and {@code e2}.
         * @param distanceY The distance along the Y axis that has been scrolled since the last
         *              call to onScroll. This is NOT the distance between {@code e1}
         *              and {@code e2}.
         * @return true if the event is consumed, else false
         */
        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);//在屏幕中滑动时触发,随意移动图片就在这里触发的。

        /**
         * Notified when a long press occurs with the initial on down {@link MotionEvent}
         * that trigged it.
         *
         * @param e The initial on down motion event that started the longpress.
         */
        void onLongPress(MotionEvent e);//长按时触发。

        /**
         * Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
         * and the matching up {@link MotionEvent}. The calculated velocity is supplied along
         * the x and y axis in pixels per second.
         *
         * @param e1 The first down motion event that started the fling.
         * @param e2 The move motion event that triggered the current onFling.
         * @param velocityX The velocity of this fling measured in pixels per second
         *              along the x axis.
         * @param velocityY The velocity of this fling measured in pixels per second
         *              along the y axis.
         * @return true if the event is consumed, else false
         */
        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);//快速滑动屏幕时触发。
    }

在上面的代码中我已经解释各个函数的功能,通过这个手势工具类实现随意的拖动图片。

第一步就是在onTouchEvent中调用这个手势mScaleGestureDetector.onTouchEvent(event),这样就开始使用手势了。如何实现随意的拖动能,涉及到matrix的使用,可以详见自行百度下使用方法。

在使用matrix时,在构造函数中调用 setScaleType(ScaleType.MATRIX);否则就没有效果,看源码中说,从xml配置文件加载都必须要要加这个配置。

matrix中有个postTranslate函数,对矩阵进行位移变换的,onscroll中提供了位移变化量,所以就特别简单了,通过这个位移函数就可以实现图片的随意的拖动了。而setImageMatrix(mMatrix) 的作用就是不断加载数据然后刷新,这样才能实现真正的移动。

  • GestureDetector.OnDoubleTapListener
    还有一个功能就是双击图片放大,再次双击还原,通过GestureDetector.OnDoubleTapListener实现的。
public interface OnDoubleTapListener {
        /**
         * Notified when a single-tap occurs.
         * 

* Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this * will only be called after the detector is confident that the user's * first tap is not followed by a second tap leading to a double-tap * gesture. * * @param e The down motion event of the single-tap. * @return true if the event is consumed, else false */ boolean onSingleTapConfirmed(MotionEvent e);//单击事件触发 /** * Notified when a double-tap occurs. * * @param e The down motion event of the first tap of the double-tap. * @return true if the event is consumed, else false */ boolean onDoubleTap(MotionEvent e);//双击事件触发。 /** * Notified when an event within a double-tap gesture occurs, including * the down, move, and up events. * * @param e The motion event that occurred during the double-tap gesture. * @return true if the event is consumed, else false */ boolean onDoubleTapEvent(MotionEvent e);//一个完整的双击事件,包括down,move,up事件。根据日志感觉和上面哪个没有区别。

在onDoubleTap(MotionEvent e)中实现放大和还原功能即可,也是通过matrix实现的,详见代码。
还剩一个onContextLisenter类就不介绍了,基本上用不上。

2.ScaleGestureDetector

ScaleGestureDetector 是缩放类工具栏,通过onTouchEvent(MotioinEvent event)或onTouch(MotionEvent event)中event事件来实现手势触发。主要有以下集中方法:

  • 1.public boolean onScaleBegin(ScaleGestureDetector detector) //用处缩放开始
  • 2.public boolean onScale(ScaleGestureDetector detector)//缩放操作
  • 3.public void onScaleEnd(ScaleGestureDetector detector) //缩放手势结束
    缩放操作分为三步:1,缩放开始,检测是否继续进行缩放操作,返回为true的话才会进行第二部,否则只执行到这一步纠结束了。2.正在缩放的操作,3.缩放结束,并获取当前的手指的位置。在demo中,实现的是图片的缩放功能,onScaleBegin中获取初始的两个手指的位置,在onScale中获取实时的手指位置,通过初始手指距离和实时的手指距离的商来判断放缩的大小,直接看代码吧,特别简单。
    使用这个放缩类有两点需要注意,一点需要在onTouchEvent(MotioinEvent event)或onTouch(MotionEvent event)中调用mScaleGestureDetector.onTouch(),这样手指才能执行。二,放缩比例的计算,其实也没什么难的。

Demo:

public class GestureImageViewer extends ImageView implements GestureDetector.OnGestureListener,
        ScaleGestureDetector.OnScaleGestureListener,
        GestureDetector.OnDoubleTapListener {
    private static final String TAG = GestureImageViewer.class.getSimpleName();
    private GestureDetector mGestureDetector = null;
    private ScaleGestureDetector mScaleGestureDetector = null;
    private Matrix mMatrix = new Matrix();
    private float startDis = 0f;
    private float oldAngle = 0f;

    public GestureImageViewer(Context context) {
        super(context);
    }

    public GestureImageViewer(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setScaleType(ScaleType.MATRIX);//这个千万不要忘记了,否则没有效果。
        mGestureDetector = new GestureDetector(context, this);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        mMatrix.set(this.getImageMatrix());
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }
        if (mScaleGestureDetector.onTouchEvent(event)) {//放缩的手势类需要在这调用下
            return true;
        }
        return true;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        Log.i(TAG, "onDown");
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        Log.i(TAG, "onShowPress");
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.i(TAG, "onSingleTapUp");
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent event, float distanceX, float distanceY) {
        Log.i(TAG, "onScroll");
        mMatrix.postTranslate(-distanceX, -distanceY);
        setImageMatrix(mMatrix);
        return false;
    }

    private float calculateAngle(MotionEvent event) {
        return (float) Math.toDegrees(Math.atan2(event.getY(0) - event.getY(1), event.getX(0) - event.getX(1)));
    }

    @Override
    public void onLongPress(MotionEvent e) {
        Log.i(TAG, "onLongPress");
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        Log.i(TAG, "onFling" + "velocityX:" + velocityX + "velocityY:" + velocityY);
        return false;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mMatrix.set(getImageMatrix());
        float scaleFactor = detector.getCurrentSpan() / startDis;
        mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
        setImageMatrix(mMatrix);
        startDis = detector.getCurrentSpan();
        Log.i(TAG, "onScale" + scaleFactor);
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        startDis = detector.getCurrentSpan();
        Log.i(TAG, "onScaleBegin");
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        Log.i(TAG, "onScaleEnd");
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        Log.i(TAG, "onSingleTapConfirmed");
        return true;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Log.i(TAG, "onDoubleTap");
        mMatrix.set(getImageMatrix());
        float[] values = new float[9];
        mMatrix.getValues(values);
        float scaleFactor = values[Matrix.MSCALE_X];
        if (scaleFactor == 1) {
            mMatrix.postScale(1.5f, 1.5f, e.getX(), e.getY());
        } else {
            mMatrix.reset();
        }
        setImageMatrix(mMatrix);
        return true;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        Log.i(TAG, "onDoubleTapEvent");
        return true;
    }
}

你可能感兴趣的:(Android)