ImageView通過matrix實現手勢縮放

Ange1Lins 8年前發布 | 9K 次閱讀 Android開發 移動開發

來自: http://www.jcodecraeer.com//a/anzhuokaifa/androidkaifa/2013/1023/1579.html


關于ImageView的手勢縮放,有很多種方法,絕大多數開源自定義縮放都是修改了ondraw函數來實現的。但是ImageView本身有scaleType屬性,通過設置android:scaleType="matrix" 可以用很少的代碼就實現縮放功能。縮放的優點是實現起來簡單,同時因為沒有反復調用ondraw函數,縮放過程中不會有閃爍現象。

MATRIX矩陣可以動態縮小放大圖片來顯示,縮小圖片:

//獲得Bitmap的高和寬
int bmpWidth=bmp.getWidth();
int bmpHeight=bmp.getHeight();

//設置縮小比例
double scale=0.8;
//計算出這次要縮小的比例
scaleWidth=(float)(scaleWidth*scale);
scaleHeight=(float)(scaleHeight*scale);

//產生resize后的Bitmap對象
Matrix matrix=new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp=Bitmap.createBitmap(bmp, 0, 0, bmpWidth, bmpHeight, matrix, true);

下面將一個自定義的實現了手勢縮放的ImageView代碼拷貝如下:

package com.jcodecraeer.stargallerry;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.widget.ImageView;
public class ImageTouchView extends ImageView {

    private PointF startPoint = new PointF();
    private Matrix matrix = new Matrix();
    private Matrix currentMaritx = new Matrix();

    private int mode = 0;//用于標記模式
    private static final int DRAG = 1;//拖動
    private static final int ZOOM = 2;//放大
    private float startDis = 0;
    private PointF midPoint;//中心點

    /**
     * 默認構造函數
     * @param context
     */
    public ImageTouchView(Context context){
        super(context);
    }
    /**
     * 該構造方法在靜態引入XML文件中是必須的
     * @param context
     * @param paramAttributeSet
     */
    public ImageTouchView(Context context,AttributeSet paramAttributeSet){
        super(context,paramAttributeSet);
    }

    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mode = DRAG;
            currentMaritx.set(this.getImageMatrix());//記錄ImageView當期的移動位置
            startPoint.set(event.getX(),event.getY());//開始點
            break;
        case MotionEvent.ACTION_MOVE://移動事件

            if (mode == DRAG) {//圖片拖動事件
                float dx = event.getX() - startPoint.x;//x軸移動距離
                float dy = event.getY() - startPoint.y;
                matrix.set(currentMaritx);//在當前的位置基礎上移動
                matrix.postTranslate(dx, dy);

            } else if(mode == ZOOM){//圖片放大事件
                float endDis = distance(event);//結束距離
                if(endDis > 10f){
                    float scale = endDis / startDis;//放大倍數
                    //Log.v("scale=", String.valueOf(scale));
                    matrix.set(currentMaritx);
                    matrix.postScale(scale, scale, midPoint.x, midPoint.y);
                }


            }
            break;

        case MotionEvent.ACTION_UP:
            mode = 0;
            break;
        //有手指離開屏幕,但屏幕還有觸點(手指)
        case MotionEvent.ACTION_POINTER_UP:
            mode = 0;
            break;
        //當屏幕上已經有觸點(手指),再有一個手指壓下屏幕
        case MotionEvent.ACTION_POINTER_DOWN:
            mode = ZOOM;
            startDis = distance(event);

            if(startDis > 10f){//避免手指上有兩個繭
                midPoint = mid(event);
                currentMaritx.set(this.getImageMatrix());//記錄當前的縮放倍數
            }

            break;
        }
        this.setImageMatrix(matrix);
        return true;
    }

    /**
     * 兩點之間的距離
     * @param event
     * @return
     */
    private static float distance(MotionEvent event){
        //兩根線的距離
        float dx = event.getX(1) - event.getX(0);
        float dy = event.getY(1) - event.getY(0);
        return FloatMath.sqrt(dx*dx + dy*dy);
    }
    /**
     * 計算兩點之間中心點的距離
     * @param event
     * @return
     */
    private static PointF mid(MotionEvent event){
        float midx = event.getX(1) + event.getX(0);
        float midy = event.getY(1) - event.getY(0);

        return new PointF(midx/2, midy/2);
    }
}

 

在xml中這樣使用自定義的ImageView:

<com.jcodecraeer.stargallerry.ImageTouchView
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="matrix"
    />

這里有個小細節, android:layout_width和android:layout_height這里都是="match_parent" ,如果我們換成wrap_content的話,你會發現圖片只能在一個很小的區間縮放。而match_parent則可以很隨意的在整個屏幕縮放。

但是match_parent導致了一個讓人意外的問題:

如果將imageView的寬度和高度都設置為填充整個父控件,然后scaleType設置成Matrix,則圖片不是居中顯示的,整個圖片靠上去了,這里我還沒有找出原因,不過在網上找出了解決的辦法:

先設置ImageView的ScaleType="CENTER"
要給控件添加拖動與放大縮水前。再把ScaleType設為:"Matric"
(即ImageView設置OnTouchListener前更改屬性即可)

其中“ImageView設置OnTouchListener前更改屬性”在我們這個例子中應替換為在caseMotionEvent.ACTION_MOVE://移動事件開始更改屬性。在代碼中更改ScaleType應該這樣做:

this.setScaleType(ImageView.ScaleType.MATRIX);

說到ScaleType,我們看看ImageView.ScaleType 值的意義和區別:

CENTER /center  按圖片的原來size居中顯示,當圖片長/寬超過View的長/寬,則截取圖片的居中部分顯示

CENTER_CROP / centerCrop  按比例擴大圖片的size居中顯示,使得圖片長(寬)等于或大于View的長(寬)

CENTER_INSIDE / centerInside  將圖片的內容完整居中顯示,通過按比例縮小或原來的size使得圖片長/寬等于或小于View的長/寬

FIT_CENTER / fitCenter  把圖片按比例擴大/縮小到View的寬度,居中顯示

FIT_END / fitEnd   把圖片按比例擴大/縮小到View的寬度,顯示在View的下部分位置

FIT_START / fitStart  把圖片按比例擴大/縮小到View的寬度,顯示在View的上部分位置

FIT_XY / fitXY  把圖片不按比例擴大/縮小到View的大小顯示

MATRIX / matrix 用矩陣來繪制

 

 

 本文由用戶 Ange1Lins 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!