首页 > 其他 > 详细

自定义ImageView 手势 缩放 滑动 矩阵

时间:2017-03-13 18:55:31      阅读:345      评论:0      收藏:0      [点我收藏+]

功能

初始时大小控制,图片宽或高大于view的,缩小至view大小,否则按原始大小显示
双击放大,第一次双击后将图片宽或高放大到view的宽或高的比例
再次双击会再在此前基础上放大固定的倍数
放大两次后后再次双击可缩小到初始大小
双指可对图片大小进行缩放,可设置控制最大缩放倍数
根据双击点的不同控制放大时的中心点
放大后,若图片大小超出view大小,则可将图片滑动到指定区域
可在view区域随意拖动,且可控制边缘,防止图片边缘滑到view边缘之内的区域
解决和ViewPage滑到事件的冲突

使用案例

public class MainActivity extends Activity {
    private int[] mImgs = new int[] { R.drawable.small, R.drawable.vertical, R.drawable.middle, R.drawable.big };
    private ImageView[] mImageViews = new ImageView[mImgs.length];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ViewPager mViewPager = new ViewPager(this);
        mViewPager.setAdapter(new PagerAdapter() {
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                ZoomImageView imageView = new ZoomImageView(getApplicationContext());
                imageView.setImageResource(mImgs[position]);
                container.addView(imageView);
                mImageViews[position] = imageView;
                return imageView;
            }
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView(mImageViews[position]);
            }
            @Override
            public boolean isViewFromObject(View arg0, Object arg1) {
                return arg0 == arg1;
            }
            @Override
            public int getCount() {
                return mImgs.length;
            }
        });
        setContentView(mViewPager);
    }
}

View代码

public class ZoomImageView extends ImageView implements OnTouchListener, OnGlobalLayoutListener {
    private static final String TAG = "bqt";
    /**一些边界缩放倍数,SCALE_FULL的效果为:第一次双击后将图片宽或高放大到view的宽或高的比例*/
    private float SCALE_FULLSCALE_DOUBLESCALE_MAX;
    /** 初始化时【屏幕/图片】的大小,也是最后一次双击时使用的缩放比例。如果图片宽高大于屏幕宽高,此值将小于1 */
    private float SCALE_INIT;
    /**是否限制双指缩放时的缩放倍数*/
    private boolean isLimitedScale = true;
    private Matrix mScaleMatrix = new Matrix();
    private final float[] matrixValues = new float[9];//用于存放矩阵的9个值
    /** 缩放的手势检测 */
    private ScaleGestureDetector mScaleGestureDetector;
    private GestureDetector mGestureDetector;
    private boolean isAutoScale;
    private int mTouchSlop;
    private float mLastXmLastY;
    private boolean isCanDrag;
    private int lastPointerCount;
    private boolean isCheckTopAndBottom = trueisCheckLeftAndRight = true;
    public ZoomImageView(Context context) {
        this(context, null);
    }
    public ZoomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setScaleType(ScaleType.MATRIX);//用矩阵来绘制
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {//双击时
                if (isAutoScale == true) return true;
                float x = e.getX();
                float y = e.getY();
                Log.i(TAG"双击onDoubleTap," + "缩放比例:" + getScale() + " , " + SCALE_INIT);
                if (getScale() < SCALE_FULL) post(new AutoScaleRunnable(SCALE_FULL, x, y));
                else if (getScale() >= SCALE_FULL && getScale() < SCALE_DOUBLE) post(new AutoScaleRunnable(SCALE_DOUBLE, x, y));
                else post(new AutoScaleRunnable(SCALE_INIT, x, y));
                isAutoScale = true;
                return true;
            }
        });
        mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {//双指缩放时
                if (getDrawable() == null) return true;
                float scale = getScale();
                float scaleFactor = detector.getScaleFactor();
                //缩放的范围控制
                if (!isLimitedScale) mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
                else if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > SCALE_INIT && scaleFactor < 1.0f)) {
                    //最大值最小值判断
                    if (scaleFactor * scale < SCALE_INIT) scaleFactor = SCALE_INIT / scale;
                    if (scaleFactor * scale > SCALE_MAX) scaleFactor = SCALE_MAX / scale;
                    //设置缩放比例
                    mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
                    checkBorderAndCenterWhenScale();
                    setImageMatrix(mScaleMatrix);
                }
                return true;
            }
        });
        this.setOnTouchListener(this);
    }
    @Override
    protected void onAttachedToWindow() {//在onDraw前调用的。也就是我们写的View在没有绘制出来时调用的
        super.onAttachedToWindow();
        Log.i(TAG"onAttachedToWindow");
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
    @Override
    public void onGlobalLayout() {//根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心
        if (getDrawable() == null) return;
        // 图片的宽高
        int dw = getDrawable().getIntrinsicWidth();
        int dh = getDrawable().getIntrinsicHeight();
        Log.i(TAG"view大小:" + getWidth() + " * " + getHeight() + " ,图片大小: " + dw + " * " + dh);
        //初始化缩放比例
        if (dw >= getWidth() && dh >= getHeight()) {// 如果图片的宽【和】高都大于view,则让其按按比例适应屏幕大小
            SCALE_INIT = Math.min(getWidth() * 1.0f / dw, getHeight() * 1.0f / dh);
            SCALE_FULL = Math.max(getWidth() * 1.0f / dw, getHeight() * 1.0f / dh);
        } else if (dw >= getWidth()) { // 如果图片的宽【或】高大于view,则缩放至屏幕的宽或者高
            SCALE_INIT = getWidth() * 1.0f / dw;
            SCALE_FULL = getHeight() * 1.0f / dh;
        } else if (dh >= getHeight()) {
            SCALE_INIT = getHeight() * 1.0f / dh;
            SCALE_FULL = getWidth() * 1.0f / dw;
        } else {//其他情况,也即小图片时,默认不进行缩放
            SCALE_INIT = 1.0f;
            SCALE_FULL = Math.min(getWidth() * 1.0f / dw, getHeight() * 1.0f / dh);
        }
        SCALE_DOUBLE = 1.5f * SCALE_FULL;//第二次双击时的缩放比例
        SCALE_MAX = 3.5f * SCALE_FULL;//最大缩放比例
        Log.i(TAG"缩放比例SCALE_INIT = " + SCALE_INIT + "--双击缩放比例:" + SCALE_FULL);
        // 将图片移动至屏幕中心,以SCALE_INIT为比例进行缩放
        mScaleMatrix.postTranslate((getWidth() - dw) / 2, (getHeight() - dh) / 2);
        mScaleMatrix.postScale(SCALE_INITSCALE_INIT, getWidth() / 2, getHeight() / 2);
        setImageMatrix(mScaleMatrix);
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) return true;//双击时,不再响应其他操作
        mScaleGestureDetector.onTouchEvent(event);
        float x = 0, y = 0;
        // 拿到触摸点的个数
        final int pointerCount = event.getPointerCount();
        // 得到多个触摸点的x与y均值
        for (int i = 0; i < pointerCount; i++) {
            x += event.getX(i);
            y += event.getY(i);
        }
        x = x / pointerCount;
        y = y / pointerCount;
        /** 每当触摸点发生变化时,重置mLasX , mLastY */
        if (pointerCount != lastPointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }
        lastPointerCount = pointerCount;
        RectF rectF = getMatrixRectF();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (rectF.width() > getWidth() || rectF.height() > getHeight()) getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (rectF.width() > getWidth() || rectF.height() > getHeight()) getParent().requestDisallowInterceptTouchEvent(true);
            Log.i(TAG"ACTION_MOVE");
            float dx = x - mLastX;
            float dy = y - mLastY;
            if (!isCanDrag) isCanDrag = isCanDrag(dx, dy);
            if (isCanDrag) {
                if (getDrawable() != null) {
                    isCheckLeftAndRight = isCheckTopAndBottom = true;
                    // 如果宽度小于屏幕宽度,则禁止左右移动
                    if (rectF.width() < getWidth()) {
                        dx = 0;
                        isCheckLeftAndRight = false;
                    }
                    // 如果高度小雨屏幕高度,则禁止上下移动
                    if (rectF.height() < getHeight()) {
                        dy = 0;
                        isCheckTopAndBottom = false;
                    }
                    /**移动时的缩放比例*/
                    float translateScale = getScale();
                    if (translateScale > 2.0f) translateScale = 2.0f;
                    if (translateScale < 0.5f) translateScale = 0.5f;
                    mScaleMatrix.postTranslate(dx * translateScale, dy * translateScale);//非常有用的设置,当放大很多倍时可以快速的滑动
                    checkMatrixBounds();
                    setImageMatrix(mScaleMatrix);
                }
            }
            mLastX = x;
            mLastY = y;
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            Log.i(TAG"ACTION_UP");
            lastPointerCount = 0;
            break;
        }
        return true;
    }
    //**************************************************************************************************************************
    /** 在缩放时,进行图片显示范围的控制,防止图片宽高大于view时,图片与控件间出现白边;防止图片小于view时不居中 */
    private void checkBorderAndCenterWhenScale() {
        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;
        int width = getWidth();
        int height = getHeight();
        // 如果宽或高大于屏幕,则控制范围
        if (rect.width() >= width) {
            if (rect.left > 0) deltaX = -rect.left;
            if (rect.right < width) deltaX = width - rect.right;
        }
        if (rect.height() >= height) {
            if (rect.top > 0) deltaY = -rect.top;
            if (rect.bottom < height) deltaY = height - rect.bottom;
        }
        // 如果宽或高小于屏幕,则让其居中
        if (rect.width() < width) deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
        if (rect.height() < height) deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
        Log.i(TAG"deltaX = " + deltaX + " , deltaY = " + deltaY);
        mScaleMatrix.postTranslate(deltaX, deltaY);
    }
    /** 根据当前图片的Matrix获得图片的范围 */
    private RectF getMatrixRectF() {
        Matrix matrix = mScaleMatrix;
        RectF rect = new RectF();
        Drawable d = getDrawable();
        if (null != d) {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rect);
        }
        return rect;
    }
    /** 获得当前的缩放比例 */
    public final float getScale() {
        mScaleMatrix.getValues(matrixValues);
        return matrixValues[Matrix.MSCALE_X];
    }
    /** 移动时,进行边界判断,主要判断宽或高大于屏幕的 */
    private void checkMatrixBounds() {
        RectF rect = getMatrixRectF();
        float deltaX = 0, deltaY = 0;
        final float viewWidth = getWidth();
        final float viewHeight = getHeight();
        // 判断移动或缩放后,图片显示是否超出屏幕边界
        if (rect.top > 0 && isCheckTopAndBottom) deltaY = -rect.top;
        if (rect.bottom < viewHeight && isCheckTopAndBottom) deltaY = viewHeight - rect.bottom;
        if (rect.left > 0 && isCheckLeftAndRight) deltaX = -rect.left;
        if (rect.right < viewWidth && isCheckLeftAndRight) deltaX = viewWidth - rect.right;
        mScaleMatrix.postTranslate(deltaX, deltaY);
    }
    /** 是否是拖动行为 */
    private boolean isCanDrag(float dx, float dy) {
        return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
    }
    //**************************************************************************************************************************
    /** 自动缩放的任务*/
    private class AutoScaleRunnable implements Runnable {
        private float mTargetScale;
        private int TIME = 100;//分几次缩放到指定的的比例
        private float tmpScale;//
        private float xy;//缩放的中心
        /**传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小 */
        public AutoScaleRunnable(float targetScale, float x, float y) {
            this.mTargetScale = targetScale;
            this.x = x;
            this.y = y;
            if (getScale() < mTargetScale) tmpScale = 1.0f + (mTargetScale - getScale() / TIME);
            else tmpScale = 1.0f - (getScale() - mTargetScale / TIME);
        }
        @Override
        public void run() {
            // 进行缩放
            mScaleMatrix.postScale(tmpScaletmpScalexy);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
            // 如果值在合法范围内,继续缩放
            if ((tmpScale > 1f && getScale() < mTargetScale) || (tmpScale < 1f && getScale() > mTargetScale)) {
                post(this);
            } else { // 设置为目标的缩放比例
                float deltaScale = mTargetScale / getScale();
                mScaleMatrix.postScale(deltaScale, deltaScale, xy);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;
            }
        }
    }
}

附件列表

     

    自定义ImageView 手势 缩放 滑动 矩阵

    原文:http://www.cnblogs.com/baiqiantao/p/6544347.html

    (0)
    (0)
       
    举报
    评论 一句话评论(0
    关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
    © 2014 bubuko.com 版权所有
    打开技术之扣,分享程序人生!