• 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!

  • 相关阅读:
    转DataBinder.Eval总结
    ASP.NET环境下XML导出导入数据方法
    iOS开发中GCD在多线程方面的理解
    hdu 1678 优先队列
    hdu 1565 状态压缩DP
    hdu 1175 连连看
    hdu 1078 记忆化搜索
    hdu 1506
    hdu 1521 记忆化搜索
    三大博弈[转]
  • 原文地址:https://www.cnblogs.com/xingyyy/p/4334873.html
Copyright © 2020-2023  润新知