1. 程式人生 > >Android中自定義點選縮放ImageView

Android中自定義點選縮放ImageView

我們平時在開發中,有時候需要在點選控制元件後有一個縮放效果,當然,有些朋友會講這很簡單啊,在資原始檔中給兩個不同的背景,用selecter作為切換,可以達到需要的效果,或者用動畫,這也很好的實現需求。但是,今天我們要做的這個點選縮放控制元件不單單是縮放那麼簡單,請看效果圖!
點選邊緣縮放效果點選中心縮放效果
首先,要實現以上效果,我們要自定義View繼承Imageview,通過Imageview的Matrix來設定圖片的縮放,旋轉,平移等等。開始初始化引數:

   /**
     * 是否執行中
     */
    private boolean onAnimation = true;
    /**
     * 旋轉角度
     */
private int rotateDegree = 10; private boolean isFirst = true; /** * 比例 */ private float minScale = 0.95f; /** * 控制元件寬度 */ private int vWidth; /** * 控制元件高度 */ private int vHeight; private boolean isFinish = true, isActionMove = false, isScale = false
; /** * 相機api */ private Camera camera; boolean XbigY = false; float RolateX = 0; float RolateY = 0;

這裡要說明一下,Camera類在這裡,主要用於,圖片的處理。好 了,接下來初始化構造方法,同時Camera也創建出來。

 public MyImageView(Context context) {
        super(context);
        camera = new Camera();
    }

    public
MyImageView(Context context, AttributeSet attrs) { super(context, attrs); camera = new Camera(); }

這樣我們就可以開始在ondraw()裡面操作各種資料了,下面重寫ondraw()並在裡面初始化,這裡有一個判斷是否是第一次展示,用了isFirst來做標誌位,如果第一次就初始化引數,如果不是就 不再初始化引數了。當然這個直接寫在構造方法裡面也可以。

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isFirst) {
            isFirst = false;
            //初始化
            init();
        }
        //為畫布設定抗鋸齒
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
                | Paint.FILTER_BITMAP_FLAG));
    }
  /**
     * 初始化
     */
    public void init() {
        vWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        vHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        Drawable drawable = getDrawable();
        BitmapDrawable bd = (BitmapDrawable) drawable;
        /**畫筆設定抗鋸齒
         */
        bd.setAntiAlias(true);
    }

上面的init()方法中getDrawable()方法作用就是得到Imageview的顯示圖片。這下我們就應該開始處理點選事件了。重寫OnTuchEvent()在裡面分別處理Down,Move,Up事件。

@Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        if (!onAnimation)
            return true;

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                float X = event.getX();
                float Y = event.getY();
                RolateX = vWidth / 2 - X;
                RolateY = vHeight / 2 - Y;
                XbigY = Math.abs(RolateX) > Math.abs(RolateY) ? true : false;
                //這裡判斷縮放區域
                isScale = X > vWidth / 3 && X < vWidth * 2 / 3 && Y > vHeight / 3 && Y < vHeight * 2 / 3;
                isScale = true;
                isActionMove = false;

                if (isScale) {
                    handler.sendEmptyMessage(1);
                } else {
                    rolateHandler.sendEmptyMessage(1);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float x = event.getX();
                float y = event.getY();
                if (x > vWidth || y > vHeight || x < 0 || y < 0) {
                    isActionMove = true;
                } else {
                    isActionMove = false;
                }

                break;
            case MotionEvent.ACTION_UP:
                if (isScale) {
                    handler.sendEmptyMessage(6);
                } else {
                    rolateHandler.sendEmptyMessage(6);
                }
                break;
        }
        return true;
    }

當檢測到點選區域位於View的位置後,就通過handler傳送訊息,在handler裡面處理,縮放操作。在這裡我們分為兩個效果,1.點選View邊緣,所在邊緣縮放效果。2.點選View中心四周縮放。這樣rolateHandler用於邊緣縮放,handler用於四周縮放。

private Handler handler = new Handler() {
        private Matrix matrix = new Matrix();
        private float s;
        int count = 0;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            matrix.set(getImageMatrix());
            switch (msg.what) {
                case 1:
                    if (!isFinish) {
                        return;
                    } else {
                        isFinish = false;
                        count = 0;
                        s = (float) Math.sqrt(Math.sqrt(minScale));
                        //縮放操作
                        BeginScale(matrix, s);
                        handler.sendEmptyMessage(2);
                    }
                    break;
                case 2:
                    BeginScale(matrix, s);
                    if (count < 4) {
                        handler.sendEmptyMessage(2);
                    } else {
                        isFinish = true;
                        if (!isActionMove && onclick != null) {
                            onclick.onClick();
                        }
                    }
                    count++;
                    break;
                case 6:
                    if (!isFinish) {
                        handler.sendEmptyMessage(6);
                    } else {
                        isFinish = false;
                        count = 0;
                        s = (float) Math.sqrt(Math.sqrt(1.0f / minScale));
                        //縮放操作
                        BeginScale(matrix, s);
                        handler.sendEmptyMessage(2);
                    }
                    break;
            }
        }
    };

用於邊緣縮放的handler類

private Handler rolateHandler = new Handler() {
        private Matrix matrix = new Matrix();
        private float count = 0;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            matrix.set(getImageMatrix());
            switch (msg.what) {
                case 1:
                    count = 0;
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    rolateHandler.sendEmptyMessage(2);
                    break;
                case 2:
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    if (count < getDegree()) {
                        rolateHandler.sendEmptyMessage(2);
                    } else {
                        isFinish = true;
                    }
                    count++;
                    count++;
                    break;
                case 3:
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    if (count > 0) {
                        rolateHandler.sendEmptyMessage(3);
                    } else {
                        isFinish = true;
                      //  if (!isActionMove && onclick != null) {
                      //      onclick.onClick();
                     //   }
                    }
                    count--;
                    count--;
                    break;
                case 6:
                    count = getDegree();
                    //縮放操作
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    rolateHandler.sendEmptyMessage(3);
                    break;
            }
            Log.e("renk", "rolate>" + msg.what);
        }
    };

上面都呼叫了縮放操作方法,下面看縮放操作方法的具體實現程式碼

 private synchronized void BeginRolate(Matrix matrix, float rolateX,
                                          float rolateY) {
        // Bitmap bm = getImageBitmap();
        int scaleX = (int) (vWidth * 0.5f);
        int scaleY = (int) (vHeight * 0.5f);
        //儲存狀態,與restore()成對出現
        camera.save();
        camera.rotateX(RolateY > 0 ? rolateY : -rolateY);
        //圍繞Y軸作的旋轉
        camera.rotateY(RolateX < 0 ? rolateX : -rolateX);
        //將camera產生的效果加於引數matrix上
        camera.getMatrix(matrix);
        //在使用camera設定動畫之前,進行了camera.save(),將狀態恢復到儲存的狀態。
        camera.restore();
        // 控制中心點
        if (RolateX > 0 && rolateX != 0) {
            matrix.preTranslate(-vWidth, -scaleY);
            matrix.postTranslate(vWidth, scaleY);
        } else if (RolateY > 0 && rolateY != 0) {
            matrix.preTranslate(-scaleX, -vHeight);
            matrix.postTranslate(scaleX, vHeight);
        } else if (RolateX < 0 && rolateX != 0) {
            matrix.preTranslate(-0, -scaleY);
            matrix.postTranslate(0, scaleY);
        } else if (RolateY < 0 && rolateY != 0) {
            matrix.preTranslate(-scaleX, -0);
            matrix.postTranslate(scaleX, 0);
        }
        setImageMatrix(matrix);
        Log.e("renk", "rolate>BeginRolate()");
    }
    private synchronized void BeginScale(Matrix matrix, float scale) {
        int scaleX = (int) (vWidth * 0.5f);
        int scaleY = (int) (vHeight * 0.5f);
        //縮放圖片動作
        //設定Matrix以(px,py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
        matrix.postScale(scale, scale, scaleX, scaleY);
        //設定圖片矩陣
        setImageMatrix(matrix);
    }

Matrix提供了一些方法來控制圖片變換:
setTranslate(float dx, float dy)
:控制Matrix進行位移。
setSkew(float kx, float ky)
:控制Matrix進行傾斜,kx、ky為X、Y方向上的比例。
setSkew(float kx, float ky, float px, float py)
:控制Matrix以px、py為軸心進行傾斜,kx、ky為X、Y方向上的傾斜比例。
setRotate(float degrees)
:控制Matrix進行depress角度的旋轉,軸心為(0,0)。
setRotate(float degrees, float px, float py)
:控制Matrix進行depress角度的旋轉, 軸心為(px, py) 。
setScale(float sx, float sy)
:設定Matrix進行縮放,sx、sy為X、Y方向上的縮放比例。
setScale(float sx, float sy, float px, float py) :
設定Matrix以(px, py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
注意:以上的set方法,均有對應的post和pre方法,Matrix呼叫一系列set,pre,post方法時,可視為將這些方法插入到一個佇列.當然,按照佇列中從頭至尾的順序呼叫執行. 其中pre表示在隊頭插入一個方法,post表示在隊尾插入一個方法.而set表示把當前佇列清空,並且總是位於佇列的最中間位置.當執行了一次set後:pre方法總是插入到set前部的佇列的最前面, post方法總是插入到set後部的佇列的最後面。
現在基本上功能就完成了,但是,如果在使用的時候,給其設定setonclicklistener()是不能成功的,因為我們在自定義中重寫了OntuchEvent(),所以要設定監聽就要自己寫個介面然後設定,先定義一個介面。並初始化。

/**
     * 點選介面
     */
    OnViewClick onclick = null;

 public interface OnViewClick {
        public void onClick();
    }
//在rolateHandler裡面新增如下方法。
isFinish = true;
  if (!isActionMove && onclick != null) {
     onclick.onClick();
    }

還要注意一下,在佈局檔案中設定imageview的scaleType為matrix中,Android中使用Matrix對影象進行縮放、旋轉、平移、斜切等變換的。關於更詳細的Matrix的講解請看這裡Matrix詳解
在最後貼一下全部程式碼。共享出來。謝謝大家

public class MyImageView extends ImageView {
    /**
     * 是否執行中
     */
    private boolean onAnimation = true;
    /**
     * 旋轉角度
     */
    private int rotateDegree = 10;

    private boolean isFirst = true;
    /**
     * 比例
     */
    private float minScale = 0.95f;
    /**
     * 控制元件寬度
     */
    private int vWidth;
    /**
     * 控制元件高度
     */
    private int vHeight;
    private boolean isFinish = true, isActionMove = false, isScale = false;
    /**
     * 相機api
     */
    private Camera camera;

    boolean XbigY = false;
    float RolateX = 0;
    float RolateY = 0;
    /**
     * 點選介面
     */
    OnViewClick onclick = null;

    public MyImageView(Context context) {
        super(context);
        camera = new Camera();
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        camera = new Camera();
    }

    public void SetAnimationOnOff(boolean oo) {
        onAnimation = oo;
    }

    public void setOnClickIntent(OnViewClick onclick) {
        this.onclick = onclick;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isFirst) {
            isFirst = false;
            init();
        }
        //為畫布設定抗鋸齒
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG
                | Paint.FILTER_BITMAP_FLAG));
    }

    /**
     * 初始化
     */
    public void init() {
        vWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        vHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        Drawable drawable = getDrawable();
        BitmapDrawable bd = (BitmapDrawable) drawable;
        /**畫筆設定抗鋸齒
         */
        bd.setAntiAlias(true);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        if (!onAnimation)
            return true;


        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                float X = event.getX();
                float Y = event.getY();
                RolateX = vWidth / 2 - X;
                RolateY = vHeight / 2 - Y;
                XbigY = Math.abs(RolateX) > Math.abs(RolateY) ? true : false;

                isScale = X > vWidth / 3 && X < vWidth * 2 / 3 && Y > vHeight / 3 && Y < vHeight * 2 / 3;
                isScale = true;
                isActionMove = false;

                if (isScale) {
                    handler.sendEmptyMessage(1);
                } else {
                    rolateHandler.sendEmptyMessage(1);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float x = event.getX();
                float y = event.getY();
                if (x > vWidth || y > vHeight || x < 0 || y < 0) {
                    isActionMove = true;
                } else {
                    isActionMove = false;
                }

                break;
            case MotionEvent.ACTION_UP:
                if (isScale) {
                    handler.sendEmptyMessage(6);
                } else {
                    rolateHandler.sendEmptyMessage(6);
                }
                break;
        }
        return true;
    }

    public interface OnViewClick {
        public void onClick();
    }

    private Handler rolateHandler = new Handler() {
        private Matrix matrix = new Matrix();
        private float count = 0;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            matrix.set(getImageMatrix());
            switch (msg.what) {
                case 1:
                    count = 0;
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    rolateHandler.sendEmptyMessage(2);
                    break;
                case 2:
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    if (count < getDegree()) {
                        rolateHandler.sendEmptyMessage(2);
                    } else {
                        isFinish = true;
                    }
                    count++;
                    count++;
                    break;
                case 3:
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    if (count > 0) {
                        rolateHandler.sendEmptyMessage(3);
                    } else {
                        isFinish = true;
                        if (!isActionMove && onclick != null) {
                            onclick.onClick();
                        }
                    }
                    count--;
                    count--;
                    break;
                case 6:
                    count = getDegree();
                    BeginRolate(matrix, (XbigY ? count : 0), (XbigY ? 0 : count));
                    rolateHandler.sendEmptyMessage(3);
                    break;
            }
            Log.e("renk", "rolate>" + msg.what);
        }
    };

    private synchronized void BeginRolate(Matrix matrix, float rolateX,
                                          float rolateY) {
        // Bitmap bm = getImageBitmap();
        int scaleX = (int) (vWidth * 0.5f);
        int scaleY = (int) (vHeight * 0.5f);
        //儲存狀態,與restore()成對出現
        camera.save();
        camera.rotateX(RolateY > 0 ? rolateY : -rolateY);
        //圍繞Y軸作的旋轉
        camera.rotateY(RolateX < 0 ? rolateX : -rolateX);
        //將camera產生的效果加於引數matrix上
        camera.getMatrix(matrix);
        //在使用camera設定動畫之前,進行了camera.save(),將狀態恢復到儲存的狀態。
        camera.restore();
        // 控制中心點
        if (RolateX > 0 && rolateX != 0) {
            matrix.preTranslate(-vWidth, -scaleY);
            matrix.postTranslate(vWidth, scaleY);
        } else if (RolateY > 0 && rolateY != 0) {
            matrix.preTranslate(-scaleX, -vHeight);
            matrix.postTranslate(scaleX, vHeight);
        } else if (RolateX < 0 && rolateX != 0) {
            matrix.preTranslate(-0, -scaleY);
            matrix.postTranslate(0, scaleY);
        } else if (RolateY < 0 && rolateY != 0) {
            matrix.preTranslate(-scaleX, -0);
            matrix.postTranslate(scaleX, 0);
        }
        setImageMatrix(matrix);
        Log.e("renk", "rolate>BeginRolate()");
    }


    private Handler handler = new Handler() {
        private Matrix matrix = new Matrix();
        private float s;
        int count = 0;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            matrix.set(getImageMatrix());
            switch (msg.what) {
                case 1:
                    if (!isFinish) {
                        return;
                    } else {
                        isFinish = false;
                        count = 0;
                        s = (float) Math.sqrt(Math.sqrt(minScale));
                        BeginScale(matrix, s);
                        handler.sendEmptyMessage(2);
                    }
                    break;
                case 2:
                    BeginScale(matrix, s);
                    if (count < 4) {
                        handler.sendEmptyMessage(2);
                    } else {
                        isFinish = true;
                        if (!isActionMove && onclick != null) {
                            onclick.onClick();
                        }
                    }
                    count++;
                    break;
                case 6:
                    if (!isFinish) {
                        handler.sendEmptyMessage(6);
                    } else {
                        isFinish = false;
                        count = 0;
                        s = (float) Math.sqrt(Math.sqrt(1.0f / minScale));
                        BeginScale(matrix, s);
                        handler.sendEmptyMessage(2);
                    }
                    break;
            }
        }
    };



    private synchronized void BeginScale(Matrix matrix, float scale) {
        int scaleX = (int) (vWidth * 0.5f);
        int scaleY = (int) (vHeight * 0.5f);
        //縮放圖片動作
        //設定Matrix以(px,py)為軸心進行縮放,sx、sy為X、Y方向上的縮放比例。
        matrix.postScale(scale, scale, scaleX, scaleY);
        //設定圖片矩陣
        setImageMatrix(matrix);
    }

    public int getDegree() {
        return rotateDegree;
    }

    public void setDegree(int degree) {
        rotateDegree = degree;
    }

    public float getScale() {
        return minScale;
    }

    public void setScale(float scale) {
        minScale = scale;
    }
}