支持手勢縮放ImageView

import android.content.Context;
import android.graphics.Bitmap;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;


public class MyImageView extends ImageView {
    static final int NONE = 0;
    // 拖動中
    static final int DRAG = 1;
    // 縮放中
    static final int ZOOM = 2;
    // 放大ing
    static final int BIGGER = 3;
    // 縮小ing
    static final int SMALLER = 4;
    // 關閉縮放動畫
    static final int OPENSCALE = 1;
    // 關閉移動動畫
    static final int OPENTRANS = 2;
    // 當前的事件
    private int mode = NONE;
    // 兩觸點距離
    private float beforeLenght;
    // 兩觸點距離
    private float afterLenght;
    // 縮放的比例 X Y方向都是這個值 越大縮放的越快
    private float scale = 0.06f;
    /* 處理拖動 變量 */
    private int screenW;
    private int screenH;
    private int start_x;
    private int start_y;
    private int stop_x;
    private int stop_y;
    private TranslateAnimation trans;
    /* Bitmap的寬高 */
    private int bmWidth;
    private int bmHeight;
    // 處理超出邊界的動畫
    private Bitmap bitmap;
    private float maxScale = 2.0f;
    private float minScale = 0.5f;
    // 記錄初始寬度,用於縮放回彈
    private int startWidth = 0;
    private float piovtX = 0.5f;
    private float piovtY = 0.5f;
    // 默認開啓所有動畫
    private int AnimSwitch = OPENSCALE | OPENTRANS;

    /**
     * 構造函數
     * 
     * @param context
     *            相關上下文
     * @param w
     *            容器的寬
     * @param h
     *            容器的高
     */
    public MyImageView(Context context, int w, int h) {
        super(context);
        this.setPadding(0, 0, 0, 0);
        screenW = w;
        screenH = h;
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        // 重置startWidth
        startWidth = 0;
        bmWidth = bm.getWidth();
        bmHeight = bm.getHeight();
        if (bitmap != null && !bitmap.isRecycled())
            bitmap.recycle();
        bitmap = bm;
    }

    /**
     * 釋放ImageView的Bitmap
     */
    public void recycle() {
        setImageBitmap(null);
        if (bitmap != null && !bitmap.isRecycled())
            bitmap.recycle();
    }

    /**
     * 計算兩點間的距離
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    private float[] center;

    /**
     * 計算兩點間的中心點
     */
    private float[] centerPostion(MotionEvent event) {
        float[] center = new float[2];
        float x = event.getX(0);
        float y = event.getY(0);
        float x1 = event.getX(1);
        float y1 = event.getY(1);
        /* x,y分別的距離 */
        center[0] = Math.abs((x1 - x) / 2);
        center[1] = Math.abs((y1 - y) / 2);
        center[0] = Math.max(x, x1) - center[0];
        center[1] = Math.max(y, y1) - center[1];
        return center;
    }

    /**
     * 設置ImageView大小等於顯示的內容大小
     */
    public void setRect() {
        float scale = Math.min((float) getWidth() / (float) bmWidth,
                (float) getHeight() / (float) bmHeight);
        int w = (int) ((float) bmWidth * scale) + 1;
        int h = (int) ((float) bmHeight * scale) + 1;
        // int t=(screenH-h)/2;
        // int l=(screenW-w)/2;
        int t = getTop();
        int l = getLeft();
        layout(l, t, l + w, t + h);
    }

    /**
     * 處理各種移動回彈
     * 
     * @param disX
     *            X的偏移
     * @param disY
     *            Y的偏移
     */
    public void Rebound(int disX, int disY) {
        this.layout(getLeft() + disX, getTop() + disY, getLeft() + disX
                + getWidth(), getTop() + disY + getHeight());
        if ((AnimSwitch & OPENTRANS) == 0)
            return;
        trans = new TranslateAnimation(-disX, 0, -disY, 0);
        trans.setInterpolator(new AccelerateInterpolator());
        trans.setDuration(300);
        this.startAnimation(trans);
    }

    /**
     * 處理各種縮放回彈
     */
    public boolean ReScale() {
        float scaleX = 1f;
        float scaleY = 1f;
        int width = getWidth();
        int height = getHeight();
        int l, t, r, b;
        if (center == null)
            return false;
        if (getWidth() > startWidth * maxScale) {
            while (getWidth() > startWidth * maxScale) {
                l = this.getLeft() + (int) (center[0] * this.getWidth());
                t = this.getTop() + (int) (center[1] * this.getHeight());
                r = this.getRight()
                        - (int) ((scale - center[0]) * this.getWidth());
                b = this.getBottom()
                        - (int) ((scale - center[1]) * this.getHeight());
                this.setFrame(l, t, r, b);
            }
            scaleX = (float) width / (float) getWidth();
            scaleY = (float) height / (float) getHeight();
        }
        if (getWidth() < startWidth * minScale) {
            while (getWidth() < startWidth * minScale) {
                l = this.getLeft() - (int) (center[0] * this.getWidth());
                t = this.getTop() - (int) (center[1] * this.getHeight());
                r = this.getRight()
                        + (int) ((scale - center[0]) * this.getWidth());
                b = this.getBottom()
                        + (int) ((scale - center[1]) * this.getHeight());
                this.setFrame(l, t, r, b);
            }
            scaleX = (float) width / (float) getWidth();
            scaleY = (float) height / (float) getHeight();
        }

        if (scaleX == 1f && scaleY == 1f)
            return false;
        if ((AnimSwitch & OPENSCALE) == 0) {
            setRect();
            onRebound();
            return true;
        }
        ScaleAnimation scaleanim = new ScaleAnimation(scaleX, 1f, scaleY, 1f,
                ScaleAnimation.RELATIVE_TO_SELF, piovtX,
                ScaleAnimation.RELATIVE_TO_SELF, piovtY);
        scaleanim.setDuration(300);
        scaleanim.setInterpolator(new AccelerateInterpolator());
        scaleanim.setAnimationListener(new AnimationListener() {
            @Override
            public void onAnimationStart(Animation paramAnimation) {
            }

            @Override
            public void onAnimationRepeat(Animation paramAnimation) {
            }

            @Override
            public void onAnimationEnd(Animation paramAnimation) {
                setRect();
                onRebound();
            }

        });
        this.startAnimation(scaleanim);
        return true;
    }

    /**
     * 處理超範圍回彈
     */
    private void onRebound() {
        /* 判斷是否超出範圍 並處理 */
        int disX = 0, disY = 0;
        if (getHeight() < screenH) {
            disY = (screenH - getHeight()) / 2 - getTop();
        }
        if (getWidth() < screenW) {
            disX = (screenW - getWidth()) / 2 - getLeft();
        }
        if (getHeight() >= screenH) {
            if (getTop() > 0)
                disY = -getTop();
            if (getBottom() < screenH)
                disY = screenH - getBottom();
        }
        if (getWidth() >= screenW) {
            if (getLeft() > 0)
                disX = -getLeft();
            if (getRight() < screenW)
                disX = screenW - getRight();
        }
        // 開始回彈
        Rebound(disX, disY);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (startWidth == 0) {
            startWidth = right - left;
            setRect();
            AnimSwitch = 0;
            onRebound();
            AnimSwitch = OPENSCALE | OPENTRANS;
        }
    }

    /**
     * 處理觸碰..
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mode = DRAG;
            stop_x = (int) event.getRawX();
            stop_y = (int) event.getRawY();
            start_x = (int) event.getX();
            start_y = stop_y - this.getTop();
            if (event.getPointerCount() == 2)
                beforeLenght = spacing(event);
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            /** 下面三句用於計算縮放中心點位置 **/
            center = centerPostion(event);
            piovtX = center[0] / getWidth();
            piovtY = center[1] / getHeight();

            center[0] = (center[0] / getWidth()) * scale;
            center[1] = (center[1] / getHeight()) * scale;
            if (spacing(event) > 10f) {
                mode = ZOOM;
                beforeLenght = spacing(event);
            }
            break;
        case MotionEvent.ACTION_UP:
            mode = NONE;
            setRect();
            /* 判斷是否超過縮放界限 */
            if (!ReScale())
                onRebound();
            break;
        case MotionEvent.ACTION_POINTER_UP:
            mode = NONE;
            break;
        case MotionEvent.ACTION_MOVE:
            /* 處理拖動 */
            if (mode == DRAG) {
                this.setPosition(stop_x - start_x, stop_y - start_y, stop_x
                        + this.getWidth() - start_x,
                        stop_y - start_y + this.getHeight());
                stop_x = (int) event.getRawX();
                stop_y = (int) event.getRawY();
            }
            /* 處理縮放 */
            //XXX
            else if (mode == ZOOM) {
                if (spacing(event) > 10f) {
                    afterLenght = spacing(event);
                    float gapLenght = afterLenght - beforeLenght;
                    if (gapLenght == 0) {
                        break;
                    } else if (Math.abs(gapLenght) > 5f) {
                        if (gapLenght > 0) {
                            if (this.getWidth()<startWidth*maxScale) {
                                this.setScale(scale, BIGGER);
                            }
                        } else {
                            if (this.getWidth()>startWidth*minScale) {
                                this.setScale(scale, SMALLER);
                            }
                        }
                        beforeLenght = afterLenght;
                    }
                }
            }
            break;
        }
        return true;
    }

    /**
     * 實現處理縮放
     */
    private void setScale(float temp, int flag) {
        int l = 0, t = 0, r = 0, b = 0;
        if (flag == BIGGER) {
            l = this.getLeft() - (int) (center[0] * this.getWidth());
            t = this.getTop() - (int) (center[1] * this.getHeight());
            r = this.getRight() + (int) ((scale - center[0]) * this.getWidth());
            b = this.getBottom()
                    + (int) ((scale - center[1]) * this.getHeight());
        } else if (flag == SMALLER) {
            l = this.getLeft() + (int) (center[0] * this.getWidth());
            t = this.getTop() + (int) (center[1] * this.getHeight());
            r = this.getRight() - (int) ((scale - center[0]) * this.getWidth());
            b = this.getBottom()
                    - (int) ((scale - center[1]) * this.getHeight());
        }
        this.setFrame(l, t, r, b);
    }

    /**
     * 實現處理拖動
     */
    private void setPosition(int left, int top, int right, int bottom) {
        this.layout(left, top, right, bottom);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章