1. 程式人生 > >Android 多點觸控(放大、縮小、旋轉、位移)

Android 多點觸控(放大、縮小、旋轉、位移)

通過多點觸控實現圖片的放大、縮小、旋轉、位移效果。

    private float oldX1 = 0;
    private float oldX2 = 0;
    private float oldY1 = 0;
    private float oldY2 = 0;
    private float oldRotation= 0;
    private boolean isDRAG = true;
    private float downX = 0;
    private float downY = 0;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int DEFAULT_MOVE = 10;// 手指移動小於該值認為沒有移動
        //必須要& MotionEvent.ACTION_MASK 才能觸發
        //ACTION_POINTER_DOWN
        switch (event.getAction()& MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (listener!=null)
                listener.onUse(true);
                oldX1 = event.getX();
                oldY1 = event.getY();
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldRotation = rotation(event);
                oldX1 = event.getX(0);
                oldX2 = event.getX(1);
                oldY1 = event.getY(0);
                oldY2 = event.getY(1);
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * 判斷是否多點觸控
                 */
                if (event.getPointerCount()>=2){
                    /**
                     * 判斷是否點選後沒有移動
                     */
                    isDRAG = false;
                    float nowDifferentX = Math.abs(event.getX(0) - event.getX(1));
                    float oldDifferentX = Math.abs(oldX1 - oldX2);
                    float nowDifferentY = Math.abs(event.getY(0) - event.getY(1));
                    float oldDifferentY = Math.abs(oldY1 - oldY2);
                    //判斷放大縮小
                    if (nowDifferentX - oldDifferentX > 0 && nowDifferentY - oldDifferentY > 0) {
                        /**
                         * 放大
                         */
                        float multiples = 1 + MULTIPLES;
                        matrix.postScale(multiples, multiples, convertCenterX(), convertCenterY());
                    } else if (nowDifferentX - oldDifferentX < 0 && nowDifferentY - oldDifferentY < 0){
                        /**
                         * 縮小
                         */
                        float multiples = 1 - MULTIPLES;
                        matrix.postScale(multiples, multiples,  convertCenterX(), convertCenterY());
                    }
                    /**
                     * rotation當前旋轉角度
                     */
                    //判斷旋轉
                    float nowRotation = rotation(event);
                    float rotation = nowRotation - oldRotation;
                    matrix.postRotate(rotation,  convertCenterX(), convertCenterY());// 旋轉
                    oldX1 = event.getX(0);
                    oldX2 = event.getX(1);
                    oldRotation += rotation;
                }else if (isDRAG){
                    /**
                     * 單指移動
                     * 新增標識isDRAG 防止當多點觸控其中一隻手指離開時 變成單點
                     */
                    matrix.postTranslate(event.getX() - oldX1, event.getY() - oldY1);
                    oldX1 = event.getX();
                    oldY1 = event.getY();
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (event.getPointerCount() == 1) {
                    isDRAG = true;
                    if (listener!=null)
                        listener.onUse(false);
                    /**
                     * 判斷是否點選後沒有移動
                     */
                    if (Math.abs(event.getX() - downX) < DEFAULT_MOVE && Math.abs(event.getY() - downY) < DEFAULT_MOVE
                            && needHead) {
                        startToAlbum();
                    }
                }
                break;
        }
        return true;
    }

優化版:旋轉和放大操作同時只能響應一個,旋轉放大原點基於手指位置計算

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int DEFAULT_MOVE = 5;// 手指移動小於該值認為沒有移動
        //必須要& MotionEvent.ACTION_MASK 才能觸發
        //ACTION_POINTER_DOWN
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (listener != null)
                    listener.onUse(true);
                oldX1 = event.getX();
                oldY1 = event.getY();
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                isOne = true;
                isScale = false;
                isRotate = false;
                isDRAG = false;
                oldRotation = rotation(event);
                oldX1 = event.getX(0);
                oldX2 = event.getX(1);
                oldY1 = event.getY(0);
                oldY2 = event.getY(1);
                float nowDifferentX = Math.abs(event.getX(0) - event.getX(1));
                float nowDifferentY = Math.abs(event.getY(0) - event.getY(1));
                mPx = nowDifferentX / 2 + Math.min(event.getX(0), event.getX(1));
                mPy = nowDifferentY / 2 + Math.min(event.getY(0), event.getY(1));
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * 判斷是否多點觸控
                 */
                if (event.getPointerCount() >= 2) {
                    /**
                     * 放大縮小
                     * 旋轉
                     * 同時只能存在一種
                     */
                    if (isOne) {
                        isScale = scale(event, false);
                        isRotate = rotate(event, false);
                        if (isScale || isRotate) {
                            isOne = false;
                        }
                    }
                    if (isScale && !isRotate) {
                        scale(event, true);
                    } else if (!isScale && isRotate) {
                        rotate(event, true);
                    } else if (isRotate) {
                        scale(event, true);
                    }
                    oldX1 = event.getX(0);
                    oldX2 = event.getX(1);
                    oldY1 = event.getY(0);
                    oldY2 = event.getY(1);
                } else if (isDRAG) {
                    /**
                     * 單指移動
                     * 新增標識isDRAG 防止當多點觸控其中一隻手指離開時 變成單點
                     */
//                    LogUtil.setLog("transX: " + (event.getX(0) - oldX1) + " transY: " + (event.getY(0) - oldY1) + " oldX:" + oldX1 + " oldY: " + oldY1 + " X: " + event.getX(0) + " Y: " + event.getY(0) + " count: " + event.getPointerCount());
                    matrix.postTranslate(event.getX(0) - oldX1, event.getY(0) - oldY1);
                    oldX1 = event.getX(0);
                    oldY1 = event.getY(0);
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (event.getPointerCount() == 1) {
                    if (listener != null)
                        listener.onUse(false);
                    /**
                     * 判斷是否點選後沒有移動
                     */
                    if (Math.abs(event.getX() - downX) < DEFAULT_MOVE && Math.abs(event.getY() - downY) < DEFAULT_MOVE
                            && needHead && isDRAG) {
                        selectDialog.show();
                    }
                    isDRAG = true;
                }
                break;
        }
        return true;
    }

    /**
     * @param isChange 是否需要改變檢視
     * @return 是否進行了放大縮小操作
     */
    private boolean scale(MotionEvent event, boolean isChange) {
        /**
         * 判斷是否點選後沒有移動
         * 兩點間距離
         */
        float nowDifferentX = Math.abs(event.getX(0) - event.getX(1));
        float oldDifferentX = Math.abs(oldX1 - oldX2);
        float nowDifferentY = Math.abs(event.getY(0) - event.getY(1));
        float oldDifferentY = Math.abs(oldY1 - oldY2);
        float changeX = nowDifferentX - oldDifferentX;
        float changeY = nowDifferentY - oldDifferentY;
        //判斷放大縮小
        float max = Math.max(Math.abs(changeX), Math.abs(changeY));
        if (changeX > 0 && changeY > 0) {
            /**
             * 放大
             */
            if (isChange) {
                float multiples = 1 + MULTIPLES * max;
                matrix.postScale(multiples, multiples, mPx, mPy);
            }
            return true;
        } else if (changeX < 0 && changeY < 0) {
            /**
             * 縮小
             */
            if (isChange) {
                float multiples = 1 - MULTIPLES * max;
                matrix.postScale(multiples, multiples, mPx, mPy);
            }
            return true;
        }
        return false;
    }

private boolean rotate(MotionEvent event, boolean isChange) {
        /**
         * rotation當前旋轉角度
         */
        //判斷旋轉
        float nowRotation = rotation(event);
        float rotation = nowRotation - oldRotation;

        if (Math.abs(rotation) >= 1) {
            if (isChange) {
                matrix.postRotate(rotation, mPx, mPy);// 旋轉
                oldRotation += rotation;
            }
            return true;
        } else return false;
    }

// 取旋轉角度
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }

放大計算方法:

當判斷多指觸控時,記錄下當前兩指的座標點,在MOVE事件觸發後,計算當前兩指之間的距離和兩指位置改變之前的距離,並進行比較,若距離變大則為放大,若距離縮小則為縮小。

旋轉計算方法:

// 取旋轉角度
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }
通過以上方法獲得當前兩指形成的角度值,在判斷多指觸控時記錄下初始角度,當觸發move事件後重新計算角度值,其差值即為旋轉了的角度。