ImageView图片拖拽缩放控件

1、在父控件为Viewpager的背景实现,所以会处理图片左右滑动和Viewpager滑动 的冲突

package com.example.widget;

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.widget.ImageView;

public class ImageControl extends ImageView {
    private String tag = getClass().getSimpleName();

    /** 用于记录开始时候的坐标位置 */
    private PointF startPoint = new PointF();
    private static final int MODE_DRAG = 1;
    /** 放大缩小照片模式 */
    private static final int MODE_ZOOM = 2;
    /** 不支持Matrix */
    private static final int MODE_UNABLE = 3;
    private static final int MODE_EXZOOM= 4;
    private int mMode = 0;//
    /** 缩放开始时的手指间距 */
    private float mStartDis;
    /** 当前Matrix */
    private Matrix mCurrentMatrix = new Matrix();
    private float[] mCurrentValues=new float[9];
    private Matrix mMatrix = new Matrix();

    private float MAX_ZOOM;
    // 这里希望双指控制不要缩小,但是Min_ZOOM!=1,因为图片可能本身就被放大过以填充控件
    private float MIN_ZOOM;
    private float DEFAULT_ZOOM;
    /** 位图对象 */
    private Bitmap mBitmap = null;
    /** 图片长度 */
    private float mImageWidth;
    /** 图片高度 */ 
    private float mImageHeight;

    private ImageListener listener;
    private float mDobleClickScale;
    private float[] mValues;

    PointF mid = new PointF();
    
    
    private XViewPager pager;
    
    public ImageControl(Context context) {
        super(context);
        init();
    }

    public ImageControl(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ImageControl(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        init();
    }

    GestureDetector mGestureDetector;

    private void init() {
        // 背景设置为balck
        setBackgroundColor(Color.BLACK);
        // 将缩放类型设置为MATRIX,表示控件内按原图原始大小在左上角(0,0)置放
        mGestureDetector = new GestureDetector(getContext(),
                new GestureListener());
    }


    @Override
    public void setImageBitmap(Bitmap bm) {
        // TODO Auto-generated method stub
        // setimageBitmap在2次onMeasure之后
        setScaleType(ScaleType.MATRIX);
        super.setImageBitmap(bm);
        // 这个判断很有必要,在activiy destroy时会setImageBitmap(null)
        if (bm == null) {
            return;
        }
        mBitmap = bm;
        // 图片原生尺寸
        mImageWidth = mBitmap.getWidth();
        mImageHeight = mBitmap.getHeight();

        fitScreen();

        // matrixA=getImageMatrix();表示matrixA指向当前的matrix对象,当getImageMatrix变化时matrixA也变了
        // matrixA.set(getImageMatrix());表示对此时的matrix的状态copy一份保存下来,无论getImageMatrix怎么变
        mMatrix.set(getImageMatrix());
        mValues = new float[9];
        mMatrix.getValues(mValues);

        MIN_ZOOM = mValues[Matrix.MSCALE_X]*2/3;
        DEFAULT_ZOOM=mValues[Matrix.MSCALE_X];
        mDobleClickScale = mValues[Matrix.MSCALE_X] * 2;
        MAX_ZOOM = mValues[Matrix.MSCALE_X] * 3;
    }
    @Override
    public void setImageResource(int resId) {
        // TODO Auto-generated method stub
        Log.i(tag, "setImageResource");
        setScaleType(ScaleType.FIT_CENTER);
        super.setImageResource(resId);
    }
    private void fitScreen() {
        // TODO Auto-generated method stub
        float scaleX = (float) getWidth() / (float) mImageWidth;
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.postScale(scaleX, scaleX);
        // 图片与下边界 的距离
        float marginY = getHeight() - scaleX * mImageHeight;
        Log.i(tag, "空余纵向边界marginY=" + marginY);
        mCurrentMatrix.getValues(mCurrentValues);
        if (marginY > 0) {
            // 说明纵向铺不满
            mCurrentMatrix.postTranslate(0, (marginY-mCurrentValues[Matrix.MTRANS_Y]) / 2f);
        }else {
            mCurrentMatrix.postTranslate(0, marginY);

        }
        // Matrix是从(0,0)左上角开始做的变换
        setImageMatrix(mCurrentMatrix);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        /** 处理单点、多点触摸 **/
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 按下事件
        case MotionEvent.ACTION_DOWN:
            // 设置拖动模式
            mMode = MODE_DRAG;
            startPoint.set(event.getX(), event.getY());
            mid.set(getWidth() / 2, getHeight() / 2);
            break;
        // 多点触摸
        case MotionEvent.ACTION_POINTER_DOWN:
            if (mMode == MODE_UNABLE)
                return true;
            mMode = MODE_ZOOM;
            pager.willIntercept=false;

            mStartDis = getDistance(event);
            midPoint(mid, event);
            break;

        case MotionEvent.ACTION_MOVE:
            if (mMode == MODE_ZOOM) {
                float endDis = getDistance(event);
                if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10
                    float scale = endDis / mStartDis;// 得到缩放倍数
                    setZoom(scale);
                    mStartDis = endDis;// 重置距离
                }

            } else if (mMode == MODE_DRAG) {
                float dx = event.getX() - startPoint.x; // 得到x轴的移动距离
                float dy = event.getY() - startPoint.y; // 得到y轴的移动距离

                // 避免和双击冲突,大于10f才算是拖动
                if (Math.sqrt(dx * dx + dy * dy) > 10f) {
                    startPoint.set(event.getX(), event.getY());
                    // 在当前基础上移动
                    mCurrentMatrix.set(getImageMatrix());
                    mCurrentMatrix.getValues(mCurrentValues);
                    Log.i(tag, "dx="+dx);

                    dx = checkDxBound(mCurrentValues, dx);
                    dy = checkDyBound(mCurrentValues, dy);
                    mCurrentMatrix.postTranslate(dx, dy);
                    
                    setImageMatrix(mCurrentMatrix);
                    Log.i(tag, "dx="+dx);
                    if(dx==0){
                        pager.willIntercept=true;
                    }else {
                        pager.willIntercept=false;
                    }
                }
                
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            // break;
        case MotionEvent.ACTION_UP:
            if(mMode==MODE_EXZOOM&&mCurrentValues[Matrix.MSCALE_X]<DEFAULT_ZOOM){
                    pager.willIntercept=true;
            }
            //至此前缩放模式结束,重置

            mMode = 0;
            break;

        // 多点松开
        case MotionEvent.ACTION_POINTER_UP:
            getImageMatrix().getValues(mCurrentValues);
            //至此缩放模式结束,进入“前缩放模式“,用于后面的ACTION_MOVE、UP
            mMode=MODE_EXZOOM;
        default:
            break;
        }

        return mGestureDetector.onTouchEvent(event);
    }

    // scale是相对于当前(不是最初)的大小来进行变化的倍率
    private void setZoom(float scale) {
        // TODO Auto-generated method stub

        float[] values = new float[9];
        // 不要getImageMatrix().getValues(values)
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.getValues(values);

        // 限制放大的最大倍数
        if (scale * values[Matrix.MSCALE_X] > MAX_ZOOM) {
            scale = MAX_ZOOM / values[Matrix.MSCALE_X];
        }
        // 限制缩小的最小倍数
        if (scale * values[Matrix.MSCALE_X] <MIN_ZOOM) {
            scale = MIN_ZOOM / values[Matrix.MSCALE_X];
        }
        if (scale == 1) {
            return;
        } 
        mCurrentMatrix.postScale(scale, scale, mid.x, mid.y);
        setImageMatrix(mCurrentMatrix);
        restrainXYBound();
    }

    @Override
    public void setImageMatrix(Matrix matrix) {
        // TODO Auto-generated method stub
        super.setImageMatrix(matrix);
        //双指缩放viewpager决不响应
        if(mMode==MODE_ZOOM){
            pager.willIntercept=false;
            return;
        }

    }
    
    // 取手势中心点
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }



    private void restrainXYBound() {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        mCurrentMatrix.set(getImageMatrix());
        mCurrentMatrix.getValues(values);
        float dx = 0, dy = 0;

        dx = checkDxBound(values, dx);
        dy = checkDyBound(values, dy);
        mCurrentMatrix.postTranslate(dx, dy);
        Log.i(tag, "dx="+dx);
        Log.i(tag, "dy="+dy);

        setImageMatrix(mCurrentMatrix);

        // float x1=(values[Matrix.MTRANS_X] + values[Matrix.MSCALE_X]
        // * mImageWidth - getWidth());
        // float y1=(values[Matrix.MTRANS_Y] + values[Matrix.MSCALE_Y]
        // * mImageHeight - getHeight());
        //
        // Log.i(tag, "x1="+x1+"y1="+y1);
        // float dx=0,dy=0;
        // if(x1>mValues[Matrix.MTRANS_X]){
        // dx=-x1-mValues[Matrix.MTRANS_X];
        // }
        // // else if(x1>mValues[Matrix.MTRANS_X]){
        // // dx=x1-mValues[Matrix.MTRANS_X];
        // // dx=-dx;
        // // }
        // if(y1>mValues[Matrix.MTRANS_Y]){
        // dy=-y1-mValues[Matrix.MTRANS_Y];
        // }
        // // else if(y1>mValues[Matrix.MTRANS_Y]){
        // // dy=y1-mValues[Matrix.MTRANS_Y];
        // // dy=-dy;
        // // }
        // mCurrentMatrix.postTranslate(dx,dy);
        // setImageMatrix(mCurrentMatrix);
        // mCurrentMatrix.set(getImageMatrix());
        // mCurrentMatrix.getValues(values);
        // x1=(values[Matrix.MTRANS_X] + values[Matrix.MSCALE_X]
        // * mImageWidth - getWidth());
        // y1=(values[Matrix.MTRANS_Y] + values[Matrix.MSCALE_Y]
        // * mImageHeight - getHeight());
        // Log.i(tag, "x1="+x1+"y1="+y1);

    }

    /**
     * 计算两个手指间的距离
     * 
     * @param event
     * @return
     */
    private float getDistance(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);
    }

    /**
     * 和当前矩阵对比,检验dx,使图像移动后不会超出ImageView边界
     * 
     * @param values
     * @param dx
     * @return
     */
    private float checkDxBound(float[] values, float dx) {
        float width = getWidth();
        if (mImageWidth * values[Matrix.MSCALE_X] < width){
            //当控件能容纳整张图片时,让图片保持正中
            dx=(getWidth()-mImageWidth*values[Matrix.MSCALE_X])/2-values[Matrix.MTRANS_X];
        }else 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) {
        // 获取ImageView控件高度
        float height = getHeight();
        // mImageHeight=sitImageBitmap方法中获取的图片高度
        // values[Matrix.MSCALE_Y]当前Y轴缩放级别计算出当前Y轴的显示高度
        if (mImageHeight * values[Matrix.MSCALE_Y] < height) {
            // 图片的纵向全部显示了
            dy=(getHeight()-mImageHeight*values[Matrix.MSCALE_Y])/2-values[Matrix.MTRANS_Y];
        }else 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;
    }



    public void onDoubleClick() {
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        // 获取当前X轴缩放级别
        float scale = values[Matrix.MSCALE_X];
        float tmp;
        if (scale > DEFAULT_ZOOM) {
            tmp = DEFAULT_ZOOM / scale;
        } else {
            tmp = mDobleClickScale / scale;
        }
        setZoom(tmp);
    }

    private class GestureListener extends SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            // 捕获Down事件
            return true;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            // 触发双击事件
            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);
            if (listener != null) {
                listener.onLongPress();
            }

        }

        @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);
        }
    }

    public interface ImageListener {
        void onLongPress();
    }

    public ImageListener getListener() {
        return listener;
    }

    public void setListener(ImageListener listener) {
        this.listener = listener;
    }

    public XViewPager getPager() {
        return pager;
    }

    public void setPager(XViewPager pager) {
        this.pager = pager;
    }

}

Viewpager

package com.example.widget;

import android.content.Context;
import android.graphics.Matrix;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

public class XViewPager extends ViewPager {
    public boolean willIntercept = true;
    private String tag = "XViewPager";

    public XViewPager(Context context) {
        super(context);
    }

    public XViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.i(tag, "willIntercept---" + willIntercept);

        /** 处理单点、多点触摸 **/
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 按下事件
        case MotionEvent.ACTION_DOWN:
            Log.i(tag, "ACTION_DOWN");
        case MotionEvent.ACTION_POINTER_DOWN:
            Log.i(tag, "ACTION_POINTER_DOWN");
        case MotionEvent.ACTION_MOVE:
            Log.i(tag, "ACTION_MOVE");

            break;
        case MotionEvent.ACTION_CANCEL:
            Log.i(tag, "ACTION_CANCEL");

             break;
        case MotionEvent.ACTION_UP:
            Log.i(tag, "ACTION_UP");
            break;

        // 多点松开
        case MotionEvent.ACTION_POINTER_UP:
            Log.i(tag, "ACTION_POINTER_UP");

        default:
            break;
        }
        if (willIntercept) {
            // 这个地方直接返回true会很卡
            return super.onInterceptTouchEvent(event);
        } else {
            // 不让viewpager响应outouch
            return false;
        }
    }

    /**
     * 设置ViewPager是否拦截点击事件
     * 
     * @param value
     *            if true, ViewPager拦截点击事件 if false,
     *            ViewPager将不能滑动,ViewPager的子View可以获得点击事件 主要受影响的点击事件为横向滑动
     * 
     */
    public void setTouchIntercept(boolean value) {
        willIntercept = value;
    }
}

 

Done!

posted @ 2015-03-13 13:18  行云有影  阅读(751)  评论(0编辑  收藏  举报