Android手势监听解析(手势控制相机变焦实践)

·  阅读 1511

需求背景

滑动放大缩小:
  1. 滑动放大:长按拍摄模式下,往上滑动手指,可以放大镜头,手指往哪个方向滑动,就放大哪个区域;
  2. 双指捻合手势放大
  3. 滑动放大提示出现时机:长按拍摄2秒后出现提示,提示显示2秒后消失;
  4. 滑动放大提示:功能上线后的版本,前三次长按拍摄时均进行提示;

68ae3616b1ae649fc9693026787d102f.png

实现方案

手势监听器

GestureDetector


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

复制代码
这些函数都在什么情况下才会触发呢:
  • OnDown(MotionEvent e) :用户按下屏幕就会触发;

  • onShowPress(MotionEvent e) :如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行,具体这个瞬间是多久,我也不清楚呃……

  • onLongPress(MotionEvent e) :长按触摸屏,超过一定时长,就会触发这个事件    触发顺序:     onDown->onShowPress->onLongPresson

  • SingleTapUp(MotionEvent e) :从名子也可以看出,一次单独的轻击抬起操作,也就是轻击一下屏幕,立刻抬起来,才会有这个触发,当然,如果除了Down以外还有其它操作,那就不再算是Single操作了,所以也就不会触发这个事件 触发顺序 点击一下非常快的(不滑动) Touchup:    onDown->onSingleTapUp->onSingleTapConfirmed     点击一下稍微慢点的(不滑动) Touchup:    onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed

  • 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轴上的移动速度,像素/秒

  • onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) :在屏幕上拖动事件。无论是用手拖动view,或者是以抛的动作滚动,都会多次触发,这个方法       在ACTION_MOVE动作发生时就会触发     滑屏:手指触动屏幕后,稍微滑动后立即松开     onDown-----》onScroll----》onScroll----》onScroll----》………----->onFling     拖动     onDown------》onScroll----》onScroll------》onFiling    可见,无论是滑屏,还是拖动,影响的只是中间OnScroll触发的数量多少而已,最终都会触发onFling事件!

代码示例
 
GestureDetector.SimpleOnGestureListener myGestureListener = new 
GestureDetector.SimpleOnGestureListener();

 GestureDetector mDetector = new GestureDetector(mContext, mListener);

 monitorView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mDetector.onTouchEvent(event);
                return false;
            }
        });       

复制代码

IGestureDetectorMonitor View手势监测器抽象接口

/**
 * Author : CharlesChen
 * Time : 2019/5/8.
 * Desc : View手势监测器
 * version : v1.0
 */
public interface IGestureDetectorMonitor {

    /**
     * 手机监听
     */
    IGestureDetector getDetector();

    /**
     * 绑定监听
     *
     * @param monitorView 绑定需要监听的View
     */
    void bindMonitorView(View monitorView);

    /**
     * 绑定监听
     *
     * @param monitorView 绑定需要监听的View
     * @param listener    外部还需要监听额外的Touch时
     */
    void bindMonitorView(View monitorView, final View.OnTouchListener listener);
}
复制代码

GestureDetectorMonitor View手势监测器

/**
 * Author : CharlesChen
 * Time : 2019/5/8.
 * Email : CharlesChen@kugou.net
 * Desc :   手势控器
 * version : v1.0
 */

public abstract class GestureDetectorMonitor implements IGestureDetectorMonitor {

    protected Context mContext;

    public GestureDetectorMonitor(Context context) {
        this.mContext = context;
    }

    public void bindMonitorView(View monitorView) {
        bindMonitorView(monitorView, null);
    }

    public void bindMonitorView(View monitorView, final View.OnTouchListener listener) {
        if (monitorView == null) {
            return;
        }
        monitorView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                onTouchEvent(event);
                if (listener != null) {
                    return listener.onTouch(v, event);
                }
                return false;
            }
        });
    }

    protected boolean onTouchEvent(MotionEvent event) {
        if (getDetector() != null) {
            return getDetector().onTouchEvent(event);
        } else {
            return false;
        }
    }
}
复制代码

CameraScrollGestureDetectorMonitor 滑动变焦监控器

/**
 * Author : CharlesChen
 * Time : 2019/5/8.
 * Email : CharlesChen@kugou.net
 * Desc : 滑动变焦监控器
 * version : v1.0
 */

public class CameraScrollGestureDetectorMonitor extends ScrollGestureDetectorMonitor {
    private static final String TAG = "CameraScrollGestureDetectorMonitor";
    private float curRawY = 0;
    private CameraZoomTrigger cameraZoomTrigger;
    private float maxDistance = 1;
    private OnZoomStateListener mZoomStateListener;
    //开始缩放标识
    private boolean isStartZoom = false;
    //是否突破上次放大倍数标识
    private boolean isZoomHolder = false;
    //起始变焦区间 0~1
    private float initialZoom = 0f;

    private GestureDetector.SimpleOnGestureListener myGestureListener = new GestureDetector.SimpleOnGestureListener() {
        private float initialY = 0;
        private float pressHeight = SystemUtils.dip2px(ApplicationController.getApplication(), 80);
        private float triggerHeight = SystemUtils.dip2px(ApplicationController.getApplication(), 40);

        @Override
        public void onLongPress(MotionEvent e) {
            FxLog.d(TAG, " onLongPress " + e.getRawY());
            //记录按下的起始点
            initialY = e.getRawY();
            //开始缩放标识
            isStartZoom = true;
            onZoomStart();
            try {//由于内部判断 如果触发了长按,就不继续响应滚动事件
                Field mInLongPress = GestureDetector.class.getDeclaredField("mInLongPress");
                mInLongPress.setAccessible(true);
                mInLongPress.setBoolean(getDetector(), false);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            super.onLongPress(e);
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (!isStartZoom) {
                return false;
            }
            curRawY = e2.getRawY();
            FxLog.d(TAG, " onScroll getRawY=" + curRawY);
            //计算滑动距离 最小触发距离triggerHeight
            float scrollDistance = initialY - curRawY - triggerHeight;
            //转换成变焦区间 0~1
            float zoomValue = CameraZoomTrigger.range(scrollDistance / (maxDistance - pressHeight));
            //是在放大还是缩小 true 放大, false 缩小
            boolean isIncrease = distanceY > 0;
            //本次变焦区间 必须大于上次 变焦值 才可以触发相机变焦(产品逻辑)
            if (initialZoom <= zoomValue) {
                isZoomHolder = false;
            }
            onZoom(zoomValue, isIncrease);
            return false;
        }
    };

    @Override
    protected boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                //手指抬起或移除区域则结束本次缩放
                if (isStartZoom) {
                    onZoomEnd();
                }
                isStartZoom = false;
                break;
        }
        return super.onTouchEvent(event);
    }

    public CameraScrollGestureDetectorMonitor(Context context) {
        super(context);
        setOnGestureListener(myGestureListener);
    }

    public void setScrollMaxDistance(int maxDistance) {
        if (maxDistance <= 0) {
            throw new RuntimeException("maxDistance 不能为小于或等于0的数");
        }
        this.maxDistance = maxDistance;
    }

    public void setCameraZoomTrigger(CameraZoomTrigger cameraZoomTrigger) {
        this.cameraZoomTrigger = cameraZoomTrigger;
    }

    private void onZoomStart() {
        if (mZoomStateListener != null) {
            mZoomStateListener.onZoomStarted();
        }
        if (cameraZoomTrigger != null) {//记录按下的当前变焦区间
            initialZoom = cameraZoomTrigger.getCameraZoom();
        }
        isZoomHolder = true;
    }

    private void onZoom(float zoomValue, boolean isIncrease) {
        FxLog.d(TAG, " onZoom  zoomValue = " + zoomValue + "  isIncrease =" + isIncrease);
        //本次变焦区间 必须大于上次 变焦值 才可以触发相机变焦(产品逻辑)
        if (!isZoomHolder && cameraZoomTrigger != null) {
            cameraZoomTrigger.setCameraZoom(zoomValue, isIncrease);
        }
        if (mZoomStateListener != null) {
            mZoomStateListener.onZoom();
        }
    }

    private void onZoomEnd() {
        FxLog.d(TAG, " onZoomEnd");
        if (mZoomStateListener != null) {
            mZoomStateListener.onZoomEnded();
        }
    }

    public void setZoomStateListener(OnZoomStateListener mZoomStateListener) {
        this.mZoomStateListener = mZoomStateListener;
    }

    public interface OnZoomStateListener {
        public void onZoomStarted();

        public void onZoom();

        public void onZoomEnded();
    }
}
复制代码

CameraScaleGestureDetectorMonitor 双指变焦监控器

/**
 * Author : CharlesChen
 * Time : 2019/5/8.
 * Desc : 双指变焦监控器
 * version : v1.0
 */

public class CameraScaleGestureDetectorMonitor extends ScaleGestureDetectorMonitor {
    /**
     * 最大放大倍数
     */
    public static final float SCALE_MAX = 4.0f;
    private final int mHeight;
    private final int mWidth;
    /**
     * 默认缩放
     */
    private float initScale = 1.0f;
    private Matrix scaleMatrix = new Matrix();
    /**
     * 处理矩阵的9个值
     */
    private float[] martiXValue = new float[9];
    private final ScaleGestureDetector.SimpleOnScaleGestureListener listener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
        private static final String TAG = "CameraScaleGesture";

        //缩放开始
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            if (cameraZoomTrigger != null) {
                //获取当前相机放大倍数
                float zoom = cameraZoomTrigger.getCameraZoom();
                //转换成缩放区间 0~1
                float scale = calculateScale(zoom);
                FxLog.d(TAG, " onScaleBegin scale =" + scale + " zoom = " + zoom);
                //同步当前缩放倍数
                scaleMatrix.reset();
                scaleMatrix.postScale(scale, scale, mWidth, mHeight);
            }
            if (scaleGestureListener != null) {
                scaleGestureListener.onScaleBegin(detector);
            }
            return super.onScaleBegin(detector);
        }

        //缩放结束
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            super.onScaleEnd(detector);
            if (scaleGestureListener != null) {
                scaleGestureListener.onScaleEnd(detector);
            }
        }

        //缩放中
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            //获取当前矩阵缩放比例
            float scale = getScale();
            //获取缩放因子
            float scaleFactor = detector.getScaleFactor();
            //计算当前的缩放区间 0~1
            float zoomValue = calculatePercent(scale);
            //是在放大还是缩小 true 放大, false 缩小
            boolean isIncrease = scaleFactor > 1;
            //通知相机缩放
            if (cameraZoomTrigger != null) {
                cameraZoomTrigger.setCameraZoom(zoomValue, isIncrease);
            }
            //计算矩阵缩放值
            if (scaleFactor * scale < initScale)
                scaleFactor = initScale / scale;
            if (scaleFactor * scale > SCALE_MAX)
                scaleFactor = SCALE_MAX / scale;
            FxLog.d(TAG, " scale =" + scale + " scaleFactor = " + scaleFactor + " zoomValue= " + zoomValue);
            //设置缩放比例
            scaleMatrix.postScale(scaleFactor, scaleFactor, mWidth, mHeight);
            if (scaleGestureListener != null) {
                scaleGestureListener.onScale(detector);
            }
            return true;
        }

        //计算当前的缩放区间 0~1
        private float calculatePercent(float scale) {
            return CameraZoomTrigger.range((scale - initScale) / (SCALE_MAX - initScale));
        }

        //区间转换成缩放倍数
        private float calculateScale(float zoomValue) {
            return zoomValue * (SCALE_MAX - initScale) + initScale;
        }
    };
    private CameraZoomTrigger cameraZoomTrigger;
    private ScaleGestureDetector.OnScaleGestureListener scaleGestureListener;

    public CameraScaleGestureDetectorMonitor(Context context) {
        super(context);
        mHeight = SystemUtils.getScreenHeight(mContext) / 2;
        mWidth = SystemUtils.getScreenWidth(mContext) / 2;
        setOnGestureListener(listener);
    }

    /**
     * 获取当前矩阵缩放比例
     */
    public float getScale() {
        scaleMatrix.getValues(martiXValue);
        return martiXValue[Matrix.MSCALE_X];
    }

    /**
     * 设置相机变焦触发器
     * @param cameraZoomTrigger 相机变焦触发器
     */
    public void setCameraZoomTrigger(CameraZoomTrigger cameraZoomTrigger) {
        this.cameraZoomTrigger = cameraZoomTrigger;
    }

    /**
     * 设置相机缩放监听
     * @param scaleGestureListener
     */
    public void setOnScaleGestureListener(ScaleGestureDetector.OnScaleGestureListener scaleGestureListener) {
        this.scaleGestureListener = scaleGestureListener;
    }
}
复制代码

CameraZoomTrigger 相机变焦的触发器

/**
 * Author : CharlesChen
 * Time : 2019/5/9.
 * Desc : 相机变焦的触发器
 * version : v1.0
 */

public class CameraZoomTrigger {

    private OnZoomListener listener;

    public CameraZoomTrigger(OnZoomListener listener){
        this.listener = listener;
    }

    public void setCameraZoom(float zoomValue, boolean isIncrease) {
        if (listener != null) {
            listener.onZoom(range(zoomValue), isIncrease);
        }
    }

    public float getCameraZoom() {
        if (listener != null) {
            return listener.getCameraZoom();
        }
        return 0;
    }

    /**
     * @param value 取[0~1]区间内的值
     * @return
     */
    public static float range(float value) {
        return Math.min(Math.max(value, 0f), 1f);
    }

   public interface OnZoomListener {
        void onZoom(float zoomValue, boolean isIncrease);

       float getCameraZoom();
    }
}
复制代码
分类:
Android
标签:
分类:
Android
标签: