Android:手把手教你打造可縮放移動的ImageView(上)


定義ImageView,實現功能如下:

1.初始化時圖片垂直居中顯示,拉伸圖片寬度至ImageView寬度。

2.使用兩根手指放大縮小圖片,可設置最大放大倍數,當圖片小於ImageView寬度時,在手指離開屏幕時恢復到ImageView寬度。

3.支持雙擊放大縮小。當圖片處於未放大狀態時,雙擊放大至指定倍數,當圖片處於放大狀態時,雙擊恢復至未放大狀態。

4.圖片拖動效果。當圖片處於未放大狀態時,不可拖動。

5.圖片拖動效果。當放大后的高度不超過ImageView時,不可垂直拖動。(由於默認設置拉伸寬度至ImageView寬度,水平方向可不判斷)。

6.圖片拖動效果。當圖片向右拖動時,若左邊緣超出左邊界,則停止水平拖動。同理上下右邊緣,即拖動后不會看到背景留白。

 

 

首先講一下原理,圖片放大縮小無非就是使用Matrix類,而這里通過手勢控制,那自然是需要監聽onTouch事件,所以原理簡單來說,就是通過監聽onTouch的各種事件來控制Matrix類了。其中具體控制方式如下:

onTouch Matrix 輔助操作
ACTION_DOWN 記錄初始點,設置本次模式為拖動模式,ScaleType設置成Matrix
ACTION_POINTER_DOWN 設置本次模式為縮放模式
ACTION_MOVE 根據模式執行postScale或postTranslate  
ACTION_UP 根據當前縮放級別決定是否重置Matrix  
雙擊 postScale  
ACTION_CANCEL 同UP  

 

 

 

 

 

 

現在開始,按功能點一一列代碼說明:

首先自定義ImageView,由於和Matrix有關,取名為MatrixImageView.java,繼承於ImageView.java

public class MatrixImageView extends ImageView{
    private final static String TAG="MatrixImageView";
    private GestureDetector mGestureDetector;
    /**  模板Matrix,用以初始化 */ 
    private  Matrix mMatrix=new Matrix();
    /**  圖片長度*/ 
    private float mImageWidth;
    /**  圖片高度 */ 
    private float mImageHeight;

    public MatrixImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        MatrixTouchListener mListener=new MatrixTouchListener();
        setOnTouchListener(mListener);
        mGestureDetector=new GestureDetector(getContext(), new GestureListener(mListener));
        //背景設置為balck
        setBackgroundColor(Color.BLACK);
        //將縮放類型設置為FIT_CENTER,表示把圖片按比例擴大/縮小到View的寬度,居中顯示
        setScaleType(ScaleType.FIT_CENTER);
    }

由於用到了雙擊縮放效果,在此引用了手勢類 GestureDetector,GestureListener就是繼承與SimpleOnGestureListener的手勢監聽類了。MatrixTouchListener繼承與onTouchListner,是Touch事件監聽的主體。在構造函數最后一句setScaleType(ScaleType.FIT_CENTER),便是滿足功能一的要點。

接下去,重寫setImageBitmap方法。

@Override
    public void setImageBitmap(Bitmap bm) {
        // TODO Auto-generated method stub
        super.setImageBitmap(bm);
        //設置完圖片后,獲取該圖片的坐標變換矩陣
        mMatrix.set(getImageMatrix());
        float[] values=new float[9];
        mMatrix.getValues(values);
        //圖片寬度為屏幕寬度除縮放倍數
        mImageWidth=getWidth()/values[Matrix.MSCALE_X];
        mImageHeight=(getHeight()-values[Matrix.MTRANS_Y]*2)/values[Matrix.MSCALE_Y];
    }

在此是為了初始化幾個重要的變量:

mMatrix:圖片的原始Matrix,記錄下來作為模板,之后的變化都是在這個Matrix的基礎上進行
mImageWidth:圖片的真實寬度,注意這個寬度是指圖片在ImageView中的真實寬度,非顯示寬度也非文件寬度。當我們把圖片放入ImageView中時,會根據ImageView的寬高進行一個轉換,轉換結果記錄在Matrix中。我們根據顯示寬度與Matrix進行計算獲得真實寬度。
mImageHeight:同寬度。

三種寬度的區別(非官方叫法,只為理解)

 

顯示寬度:ImageView中的圖片(Bitmap、Drawable)在ImageView中顯示的高度,是通過Matrix計算之后的寬度。當放大圖片時,這個寬度可能超過ImageView的寬度。
真實寬度:ImageView中的圖片在Matrix計算之前的寬度。當ImageView寬為390,圖片X軸縮放級別為0.5,一個填充滿ImageView的X軸的圖片的真實寬度為780。這個寬度和ImageView的ScaleType相關。
文件寬度:文件X軸分辨率,不一定等於真實寬度。

 

之所以在sitImageView中進行設置,是因為每次ImageView更換圖片后這些變量都會跟着改變,我們只需要和當前圖片相關的這些變量。若希望在layout中設置圖片而不是代碼中設置,請把上述代碼移至構造函數。

接下來是重點的onTouch事件:

public class MatrixTouchListener implements OnTouchListener{
        /** 拖拉照片模式 */
        private static final int MODE_DRAG = 1;
        /** 放大縮小照片模式 */
        private static final int MODE_ZOOM = 2;
        /**  不支持Matrix */ 
        private static final int MODE_UNABLE=3;
        /**   最大縮放級別*/ 
        float mMaxScale=6;
        /**   雙擊時的縮放級別*/ 
        float mDobleClickScale=2;
        private int mMode = 0;// 
        /**  縮放開始時的手指間距 */ 
        private float mStartDis;
        /**   當前Matrix*/ 
        private Matrix mCurrentMatrix = new Matrix();    

        /** 用於記錄開始時候的坐標位置 */
        private PointF startPoint = new PointF();
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //設置拖動模式
                mMode=MODE_DRAG;
                startPoint.set(event.getX(), event.getY());
               // isMatrixEnable();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                reSetMatrix();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mMode == MODE_ZOOM) {
                    setZoomMatrix(event);
                }else if (mMode==MODE_DRAG) {
                    setDragMatrix(event);
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(mMode==MODE_UNABLE) return true;
                mMode=MODE_ZOOM;
                mStartDis = distance(event);
                break;
            default:
                break;
            }

            return mGestureDetector.onTouchEvent(event);
        }

/**  
         *  計算兩個手指間的距離
         *  @param event
         *  @return   
         */
        private float distance(MotionEvent event) {
            float dx = event.getX(1) - event.getX(0);
            float dy = event.getY(1) - event.getY(0);
            /** 使用勾股定理返回兩點之間的距離 */
            return (float) Math.sqrt(dx * dx + dy * dy);
        }

 

首先可以看到,在ACTION_DOWN和ACTION_POINTER_DOWN中主要是進行當前事件模式的確定。當我們按下一個點時,會觸發Down事件,而按下第二個點后,又會觸發Action_Pointer_Down事件,在此我們把按下一個點標記為拖動事件,按下兩個點標記為縮放事件。先跟蹤縮放事件方法setZoomMatrix(event);

/**  
         *  設置縮放Matrix
         *  @param event   
         */
        private void setZoomMatrix(MotionEvent event) {
            //只有同時觸屏兩個點的時候才執行
            if(event.getPointerCount()<2) return;
            float endDis = distance(event);// 結束距離
            if (endDis > 10f) { // 兩個手指並攏在一起的時候像素大於10
                float scale = endDis / mStartDis;// 得到縮放倍數
                mStartDis=endDis;//重置距離
                mCurrentMatrix.set(getImageMatrix());//初始化Matrix
                float[] values=new float[9];
                mCurrentMatrix.getValues(values);

                scale = checkMaxScale(scale, values);
                setImageMatrix(mCurrentMatrix);    
            }
        }

首先我們判斷是否點擊了兩個點,如果不是直接返回。接着,使用distance方法計算出兩個點之間的距離。之前在Action_Pointer_Down中已經計算出了初始距離,這里計算出的是移動后的兩個點的距離。通過這兩個距離我們可以得出本次移動的縮放倍數,但這還沒完,我們需要驗證這個縮放倍數是否越界了:

/**  
         *  檢驗scale,使圖像縮放后不會超出最大倍數
         *  @param scale
         *  @param values
         *  @return   
         */
        private float checkMaxScale(float scale, float[] values) {
            if(scale*values[Matrix.MSCALE_X]>mMaxScale) 
                scale=mMaxScale/values[Matrix.MSCALE_X];
            mCurrentMatrix.postScale(scale, scale,getWidth()/2,getHeight()/2);
            return scale;
        }

我們獲取了Matrix矩陣中保存的數組,在這個數組中,values[Matrix.MSCALE_X](事實上就是數組的第0個)代表了X軸的縮放級別,判斷一下圖片當前的縮放級別再乘以剛得到的scale后是否回去越界,會的話就將其控制在邊界值。之后,以ImageView的中心點為原點,在當前Matrix的基礎上進行指定倍數的縮放。

在Move事件中縮放時我們只會阻止超越最大值的縮放,在UP事件中我們會對小於原始縮放值的縮放進行重置。方法reSetMatrix如下。

/**  
         *   重置Matrix
         */
        private void reSetMatrix() {
            if(checkRest()){
                mCurrentMatrix.set(mMatrix);
                setImageMatrix(mCurrentMatrix);
            }
        }
/**  
         *  判斷是否需要重置
         *  @return  當前縮放級別小於模板縮放級別時,重置 
         */
        private boolean checkRest() {
            // TODO Auto-generated method stub
            float[] values=new float[9];
            getImageMatrix().getValues(values);
            //獲取當前X軸縮放級別
            float scale=values[Matrix.MSCALE_X];
            //獲取模板的X軸縮放級別,兩者做比較
            mMatrix.getValues(values);
            return scale<values[Matrix.MSCALE_X];
        }

首先獲取當前X軸縮放級別(由於默認拉伸寬度至ImageView寬度,縮放級別以X軸為准),再通過模板Matrix得到原始的X軸縮放級別,判斷當前縮放級別是否小於模板縮放級別,若小於,則重置成模板縮放級別。

接下去是雙擊放大縮小圖片效果,該功能在手勢接口GestureListener中完成,主要代碼如下:

    private class  GestureListener extends SimpleOnGestureListener{
        private final MatrixTouchListener listener;
        public GestureListener(MatrixTouchListener listener) {
            this.listener=listener;
        }
        @Override
        public boolean onDown(MotionEvent e) {
            //捕獲Down事件
            return true;
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            //觸發雙擊事件
            listener.onDoubleClick();
            return true;
        }
/**  
         *   雙擊時觸發
         */
        public void onDoubleClick(){
            float scale=isZoomChanged()?1:mDobleClickScale;
            mCurrentMatrix.set(mMatrix);//初始化Matrix
            mCurrentMatrix.postScale(scale, scale,getWidth()/2,getHeight()/2);    
            setImageMatrix(mCurrentMatrix);
        }
/**  
         *  判斷縮放級別是否是改變過
         *  @return   true表示非初始值,false表示初始值
         */
        private boolean isZoomChanged() {
            float[] values=new float[9];
            getImageMatrix().getValues(values);
            //獲取當前X軸縮放級別
            float scale=values[Matrix.MSCALE_X];
            //獲取模板的X軸縮放級別,兩者做比較
            mMatrix.getValues(values);
            return scale!=values[Matrix.MSCALE_X];
        }

 

在構造函數中將onTouchListner傳遞來進來。在此只重寫兩個方法:Down和onDoubleTap,只有在Down事件中返回true,onDoubleTap才能正常觸發。

在onDoubleClick事件中,首先通過isZoomChanged方法判斷當前的縮放級別是否是模板Matrix的縮放級別,是的話將縮放倍數設置為2倍,否的話設置成1倍。在載入模板Matrix,在此基礎上做縮放。

最后是圖片拖動效果setDragMatrix()

public void setDragMatrix(MotionEvent event) {
            if(isZoomChanged()){
                float dx = event.getX() - startPoint.x; // 得到x軸的移動距離
                float dy = event.getY() - startPoint.y; // 得到x軸的移動距離
                //避免和雙擊沖突,大於10f才算是拖動
                if(Math.sqrt(dx*dx+dy*dy)>10f){
                    startPoint.set(event.getX(), event.getY());
                    //在當前基礎上移動
                    mCurrentMatrix.set(getImageMatrix());
                    float[] values=new float[9];
                    mCurrentMatrix.getValues(values);
                    dx=checkDxBound(values,dx);
                    dy=checkDyBound(values,dy);    
                    mCurrentMatrix.postTranslate(dx, dy);
                    setImageMatrix(mCurrentMatrix);
                }
            }
        }

首先是通過isZoomChanged方法判斷是否縮放過,若未縮放過則不可拖動(這種情況下圖片全貌都可以看到,不需要拖動)。接着,拿當前坐標和按下時記錄的startPoint坐標進行計算,得出拖動的距離。需要注意的是,在此需要對拖動距離做一個判斷,當其小於10f時不進行拖動,否則會和雙擊事件沖突(在雙擊事件前同樣會觸發Move事件,兩者一同執行的話,雙擊的縮放無法正常工作)。當確定開始拖動的之后,先重置startPoint的坐標,接着,開始驗證當前移動的位移量是否合法。

/**  
         *和當前矩陣對比,檢驗dx,使圖像移動后不會超出ImageView邊界
         *  @param values
         *  @param dx
         *  @return   
         */
        private float checkDxBound(float[] values,float dx) {
            float width=getWidth();
            if(mImageWidth*values[Matrix.MSCALE_X]<width)
                return 0;
            if(values[Matrix.MTRANS_X]+dx>0)
                dx=-values[Matrix.MTRANS_X];
            else if(values[Matrix.MTRANS_X]+dx<-(mImageWidth*values[Matrix.MSCALE_X]-width))
                dx=-(mImageWidth*values[Matrix.MSCALE_X]-width)-values[Matrix.MTRANS_X];
            return dx;
        }

/**  
         *  和當前矩陣對比,檢驗dy,使圖像移動后不會超出ImageView邊界
         *  @param values
         *  @param dy
         *  @return   
         */
        private float checkDyBound(float[] values, float dy) {
            float height=getHeight();
            if(mImageHeight*values[Matrix.MSCALE_Y]<height)
                return 0;
            if(values[Matrix.MTRANS_Y]+dy>0)
                dy=-values[Matrix.MTRANS_Y];
            else if(values[Matrix.MTRANS_Y]+dy<-(mImageHeight*values[Matrix.MSCALE_Y]-height))
                dy=-(mImageHeight*values[Matrix.MSCALE_Y]-height)-values[Matrix.MTRANS_Y];
            return dy;
        }

以Y軸為例,首先獲取ImageView高度,再通過sitImageBitmap方法中獲取的圖片真實高度和當前Y軸縮放級別計算出當前Y軸的顯示高度。如果顯示高度小於ImageView高度,表示當前顯示的圖片還沒有ImageView高,在Y軸不需要移動都可看清全貌,Y軸位移量直接返回0。

假如顯示高度超過了ImageView高度,獲取圖片當前在Y軸的位移量(values[Matrix.MTRANS_Y]值),將其加上計算出的位移量后是否大於0,若大於0,表示圖片上邊緣將會離開ImageView上邊緣,需要重新計算位移量。若上述條件不成立,判斷當前位移量加上計算后的位移量,是否小於圖片顯示高度-屏幕高度,若小於表示圖片下邊緣將離開ImageView下邊緣,同樣需要重新計算。最后返回計算的Y軸偏移量。X軸同理。最后使用驗證過的X、Y軸偏移量,在當前圖片Matrix的基礎上行進行偏移。

最后貼下完整代碼,Demo的話,可以到我的照相機Demo里查看,該功能為相冊功能的一部分。

package com.linj.album.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

/** 
 * @ClassName: MatrixImageView 
 * @Description:  帶放大、縮小、移動效果的ImageView
 * @author LinJ
 * @date 2015-1-7 上午11:15:07 
 *  
 */
public class MatrixImageView extends ImageView{
    private final static String TAG="MatrixImageView";
    private GestureDetector mGestureDetector;
    /**  模板Matrix,用以初始化 */ 
    private  Matrix mMatrix=new Matrix();
    /**  圖片長度*/ 
    private float mImageWidth;
    /**  圖片高度 */ 
    private float mImageHeight;

    public MatrixImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        MatrixTouchListener mListener=new MatrixTouchListener();
        setOnTouchListener(mListener);
        mGestureDetector=new GestureDetector(getContext(), new GestureListener(mListener));
        //背景設置為balck
        setBackgroundColor(Color.BLACK);
        //將縮放類型設置為FIT_CENTER,表示把圖片按比例擴大/縮小到View的寬度,居中顯示
        setScaleType(ScaleType.FIT_CENTER);
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        // TODO Auto-generated method stub
        super.setImageBitmap(bm);
        //設置完圖片后,獲取該圖片的坐標變換矩陣
        mMatrix.set(getImageMatrix());
        float[] values=new float[9];
        mMatrix.getValues(values);
        //圖片寬度為屏幕寬度除縮放倍數
        mImageWidth=getWidth()/values[Matrix.MSCALE_X];
        mImageHeight=(getHeight()-values[Matrix.MTRANS_Y]*2)/values[Matrix.MSCALE_Y];
    }

    public class MatrixTouchListener implements OnTouchListener{
        /** 拖拉照片模式 */
        private static final int MODE_DRAG = 1;
        /** 放大縮小照片模式 */
        private static final int MODE_ZOOM = 2;
        /**  不支持Matrix */ 
        private static final int MODE_UNABLE=3;
        /**   最大縮放級別*/ 
        float mMaxScale=6;
        /**   雙擊時的縮放級別*/ 
        float mDobleClickScale=2;
        private int mMode = 0;// 
        /**  縮放開始時的手指間距 */ 
        private float mStartDis;
        /**   當前Matrix*/ 
        private Matrix mCurrentMatrix = new Matrix();    

        /** 用於記錄開始時候的坐標位置 */
        private PointF startPoint = new PointF();
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //設置拖動模式
                mMode=MODE_DRAG;
                startPoint.set(event.getX(), event.getY());
                isMatrixEnable();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                reSetMatrix();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mMode == MODE_ZOOM) {
                    setZoomMatrix(event);
                }else if (mMode==MODE_DRAG) {
                    setDragMatrix(event);
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(mMode==MODE_UNABLE) return true;
                mMode=MODE_ZOOM;
                mStartDis = distance(event);
                break;
            default:
                break;
            }

            return mGestureDetector.onTouchEvent(event);
        }

        public void setDragMatrix(MotionEvent event) {
            if(isZoomChanged()){
                float dx = event.getX() - startPoint.x; // 得到x軸的移動距離
                float dy = event.getY() - startPoint.y; // 得到x軸的移動距離
                //避免和雙擊沖突,大於10f才算是拖動
                if(Math.sqrt(dx*dx+dy*dy)>10f){
                    startPoint.set(event.getX(), event.getY());
                    //在當前基礎上移動
                    mCurrentMatrix.set(getImageMatrix());
                    float[] values=new float[9];
                    mCurrentMatrix.getValues(values);
                    dx=checkDxBound(values,dx);
                    dy=checkDyBound(values,dy);    
                    mCurrentMatrix.postTranslate(dx, dy);
                    setImageMatrix(mCurrentMatrix);
                }
            }
        }

        /**  
         *  判斷縮放級別是否是改變過
         *  @return   true表示非初始值,false表示初始值
         */
        private boolean isZoomChanged() {
            float[] values=new float[9];
            getImageMatrix().getValues(values);
            //獲取當前X軸縮放級別
            float scale=values[Matrix.MSCALE_X];
            //獲取模板的X軸縮放級別,兩者做比較
            mMatrix.getValues(values);
            return scale!=values[Matrix.MSCALE_X];
        }

        /**  
         *  和當前矩陣對比,檢驗dy,使圖像移動后不會超出ImageView邊界
         *  @param values
         *  @param dy
         *  @return   
         */
        private float checkDyBound(float[] values, float dy) {
            float height=getHeight();
            if(mImageHeight*values[Matrix.MSCALE_Y]<height)
                return 0;
            if(values[Matrix.MTRANS_Y]+dy>0)
                dy=-values[Matrix.MTRANS_Y];
            else if(values[Matrix.MTRANS_Y]+dy<-(mImageHeight*values[Matrix.MSCALE_Y]-height))
                dy=-(mImageHeight*values[Matrix.MSCALE_Y]-height)-values[Matrix.MTRANS_Y];
            return dy;
        }

        /**  
         *和當前矩陣對比,檢驗dx,使圖像移動后不會超出ImageView邊界
         *  @param values
         *  @param dx
         *  @return   
         */
        private float checkDxBound(float[] values,float dx) {
            float width=getWidth();
            if(mImageWidth*values[Matrix.MSCALE_X]<width)
                return 0;
            if(values[Matrix.MTRANS_X]+dx>0)
                dx=-values[Matrix.MTRANS_X];
            else if(values[Matrix.MTRANS_X]+dx<-(mImageWidth*values[Matrix.MSCALE_X]-width))
                dx=-(mImageWidth*values[Matrix.MSCALE_X]-width)-values[Matrix.MTRANS_X];
            return dx;
        }

        /**  
         *  設置縮放Matrix
         *  @param event   
         */
        private void setZoomMatrix(MotionEvent event) {
            //只有同時觸屏兩個點的時候才執行
            if(event.getPointerCount()<2) return;
            float endDis = distance(event);// 結束距離
            if (endDis > 10f) { // 兩個手指並攏在一起的時候像素大於10
                float scale = endDis / mStartDis;// 得到縮放倍數
                mStartDis=endDis;//重置距離
                mCurrentMatrix.set(getImageMatrix());//初始化Matrix
                float[] values=new float[9];
                mCurrentMatrix.getValues(values);

                scale = checkMaxScale(scale, values);
                setImageMatrix(mCurrentMatrix);    
            }
        }

        /**  
         *  檢驗scale,使圖像縮放后不會超出最大倍數
         *  @param scale
         *  @param values
         *  @return   
         */
        private float checkMaxScale(float scale, float[] values) {
            if(scale*values[Matrix.MSCALE_X]>mMaxScale) 
                scale=mMaxScale/values[Matrix.MSCALE_X];
            mCurrentMatrix.postScale(scale, scale,getWidth()/2,getHeight()/2);
            return scale;
        }

        /**  
         *   重置Matrix
         */
        private void reSetMatrix() {
            if(checkRest()){
                mCurrentMatrix.set(mMatrix);
                setImageMatrix(mCurrentMatrix);
            }
        }

        /**  
         *  判斷是否需要重置
         *  @return  當前縮放級別小於模板縮放級別時,重置 
         */
        private boolean checkRest() {
            // TODO Auto-generated method stub
            float[] values=new float[9];
            getImageMatrix().getValues(values);
            //獲取當前X軸縮放級別
            float scale=values[Matrix.MSCALE_X];
            //獲取模板的X軸縮放級別,兩者做比較
            mMatrix.getValues(values);
            return scale<values[Matrix.MSCALE_X];
        }

        /**  
         *  判斷是否支持Matrix
         */
        private void isMatrixEnable() {
            //當加載出錯時,不可縮放
            if(getScaleType()!=ScaleType.CENTER){
                setScaleType(ScaleType.MATRIX);
            }else {
                mMode=MODE_UNABLE;//設置為不支持手勢
            }
        }

        /**  
         *  計算兩個手指間的距離
         *  @param event
         *  @return   
         */
        private float distance(MotionEvent event) {
            float dx = event.getX(1) - event.getX(0);
            float dy = event.getY(1) - event.getY(0);
            /** 使用勾股定理返回兩點之間的距離 */
            return (float) Math.sqrt(dx * dx + dy * dy);
        }

        /**  
         *   雙擊時觸發
         */
        public void onDoubleClick(){
            float scale=isZoomChanged()?1:mDobleClickScale;
            mCurrentMatrix.set(mMatrix);//初始化Matrix
            mCurrentMatrix.postScale(scale, scale,getWidth()/2,getHeight()/2);    
            setImageMatrix(mCurrentMatrix);
        }
    }


    private class  GestureListener extends SimpleOnGestureListener{
        private final MatrixTouchListener listener;
        public GestureListener(MatrixTouchListener listener) {
            this.listener=listener;
        }
        @Override
        public boolean onDown(MotionEvent e) {
            //捕獲Down事件
            return true;
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            //觸發雙擊事件
            listener.onDoubleClick();
            return true;
        }
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onSingleTapUp(e);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            // TODO Auto-generated method stub
            super.onLongPress(e);
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
                float distanceX, float distanceY) {
            return super.onScroll(e1, e2, distanceX, distanceY);
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                float velocityY) {
            // TODO Auto-generated method stub

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

        @Override
        public void onShowPress(MotionEvent e) {
            // TODO Auto-generated method stub
            super.onShowPress(e);
        }

        

        

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onDoubleTapEvent(e);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // TODO Auto-generated method stub
            return super.onSingleTapConfirmed(e);
        }

    }


}

注:當該ImageView在其他ViewGroup中,如常用的ViewPager中時,Move事件會和ViewGroup事件沖突。在划屏進行ViewPager的Item切換時,Viewpager將會通過onInterceptTouchEvent方法攔截掉,返回給ImageView一個Cancel事件,這種情況下需要重寫ViewGroup的onInterceptTouchEvent方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM