android图片预览

首先呢要介绍哈这个android端图片预览,是现在主流app必不可少的一个功能。看到一张图片,是不是就想双击放大,双手指自由缩放,额,今天就给大家带来这样一个工具类——MyImageView。


package com.cjt_pc.myimageview;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

/**
 * Created by cjt-pc on 2015/10/16.
 * Email:879309896@qq.com
 */
public class MyImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, ViewTreeObserver.OnGlobalLayoutListener {

    // 缩放的范围值
    public final float MAX_SCALE = 2.0F;
    public float MIN_SCALE = 0.6F;

    private GestureDetector mGestureDetector;
    private ScaleGestureDetector mScaleGestureDetector;
    // 每次缩放的起始比例
    private float originScale = 1f;
    // 第一次适配屏幕的比例
    private float initScale = 1f;

    /**
     * 用于存放矩阵的9个值
     */
    private final float[] matrixValues = new float[9];

    private final Matrix mScaleMatrix = new Matrix();

    private float mLastTouchX, mLastTouchY;
    private int mActivePointerId;

    // 记下第一根手指按下的坐标
    private float downX, downY;

    public MyImageView(Context context) {
        super(context);
        initValue(context);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initValue(context);
    }

    public void initValue(Context context) {
        setScaleType(ScaleType.MATRIX);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            // 双击事件
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                float tarScale;
                if ((getScale() >= MIN_SCALE && getScale() < initScale) || getScale() == MAX_SCALE) {
                    tarScale = initScale;
                } else {
                    tarScale = MAX_SCALE;
                }
                new ZoomImgTask(tarScale, e.getX(), e.getY()).execute(10);
                return true;
            }
        });
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }

        // 获得动作类型
        final int action = MotionEventCompat.getActionMasked(event);
        switch (action) {
            // 单点按下
            case MotionEvent.ACTION_DOWN: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                final float x = MotionEventCompat.getX(event, pointerIndex);
                final float y = MotionEventCompat.getY(event, pointerIndex);
                downX = mLastTouchX = x;
                downY = mLastTouchY = y;
                // 记住第一次按下手指动作的id
                mActivePointerId = MotionEventCompat.getPointerId(event, pointerIndex);
                // 父布局不允许请求拦截分发事件
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
                final float x = MotionEventCompat.getX(event, pointerIndex);
                final float y = MotionEventCompat.getY(event, pointerIndex);
                // 当手势缩放没有进行的时候才进行移动处理
                if (!mScaleGestureDetector.isInProgress()) {
                    // 当移动距离超过一定值才进行处理,防止抖动
                    if (Math.abs(x - downX) > 5 || Math.abs(y - downY) > 5) {
                        final float dx = x - mLastTouchX;
                        final float dy = y - mLastTouchY;

                        float newLeft = getMatrixRectF().left + dx;
                        float newRight = newLeft + getMatrixRectF().width();
                        if (newLeft > 0 || newRight < getWidth()) {
                            getParent().requestDisallowInterceptTouchEvent(false);
                        }

                        mScaleMatrix.postTranslate(dx, dy);
                        checkBorderAndCenterWhenScale();
                        setImageMatrix(mScaleMatrix);
                    }
                }
                mLastTouchX = x;
                mLastTouchY = y;
                break;
            }
            // 手指抬起后屏幕中至少有一个按下的点
            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);
                if (pointerId == mActivePointerId) {
                    // 若抬起的是第一次按下的pointId,则重新设置mActivePointId,并且重定位mLastTouch位置
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = MotionEventCompat.getX(event, newPointerIndex);
                    mLastTouchY = MotionEventCompat.getY(event, newPointerIndex);
                    mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex);
                }
                break;
            }
        }
        return mScaleGestureDetector.onTouchEvent(event);
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if (getDrawable() == null)
            return false;
        float scale = getScale();
        // 由于每次scaleFactor都是相对于开始缩放时的比例,为了和初始化时的比例相适应
        float scaleFactor = detector.getScaleFactor() * originScale;
        float finalScale = scaleFactor / scale;
        if (scaleFactor >= (MIN_SCALE < initScale ? MIN_SCALE : initScale) && scaleFactor <= MAX_SCALE) {
            mScaleMatrix.postScale(finalScale, finalScale, detector.getFocusX(), detector.getFocusY());
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
        }
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        // 一定要返回true才会进入onScale方法
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        originScale = getScale();
    }

    private float getScale() {
        mScaleMatrix.getValues(matrixValues);
        return matrixValues[Matrix.MSCALE_X];
    }

    /**
     * 在缩放时,进行图片显示范围的控制
     */
    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();
        }
        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());
            // 通过mapRect方法可以得到一个matrix对应比例的rectF
            matrix.mapRect(rect);
        }
        return rect;
    }

    // 当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时触发
    @Override
    public void onGlobalLayout() {
        Drawable drawable = getDrawable();
        if (drawable == null)
            return;
        int screenWidth = getWidth();
        int screenHeight = getHeight();
        int imgWidth = drawable.getIntrinsicWidth();
        int imgHeight = drawable.getIntrinsicHeight();
        // 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
        if (imgWidth > screenWidth && imgHeight <= screenHeight) {
            originScale = screenWidth * 1.0f / imgWidth;
        }
        if (imgWidth <= screenWidth && imgHeight > screenHeight) {
            originScale = screenHeight * 1.0f / imgHeight;
        }
        // 如果宽和高都大于屏幕,则让其按比例适应屏幕大小
        if (imgWidth > screenWidth && imgHeight > screenHeight) {
            originScale = Math.max(screenWidth * 1.0f / imgWidth, screenHeight * 1.0f / imgHeight);
        }
        initScale = originScale;
        // 将图片移至屏幕的中心,后面两个参数是平移的距离,而不是坐标
        mScaleMatrix.postTranslate((screenWidth - imgWidth) / 2, (screenHeight - imgHeight) / 2);
        // 设置图片长宽缩放比例
        mScaleMatrix.postScale(originScale, originScale);
        checkBorderAndCenterWhenScale();
        setImageMatrix(mScaleMatrix);
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }

    // 缩放任务,异步进行
    private class ZoomImgTask extends AsyncTask<Integer, Void, Void> {

        static final float BIGGER = 1.07f;
        static final float SMALLER = 0.93f;
        private float currentScale;
        private float mTargetScale;
        private float tmpScale;
        private float x;
        private float y;

        // 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小
        public ZoomImgTask(float targetScale, float x, float y) {
            this.mTargetScale = targetScale;
            this.x = x;
            this.y = y;
            currentScale = getScale();
            if (getScale() < mTargetScale) {
                tmpScale = BIGGER;
            } else {
                tmpScale = SMALLER;
            }
        }

        @Override
        protected Void doInBackground(Integer... params) {
            if (tmpScale == BIGGER) {
                // 放大图片至指定大小
                while (currentScale * tmpScale < mTargetScale) {
                    publishProgress();
                    try {
                        Thread.sleep(params[0]);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                tmpScale = mTargetScale / currentScale;
                publishProgress();
            } else {
                // 缩小图片至指定大小
                while (currentScale * tmpScale > mTargetScale) {
                    publishProgress();
                    try {
                        Thread.sleep(params[0]);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                tmpScale = mTargetScale / currentScale;
                publishProgress();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            // 在UI线程中设置图片并且更新currentScale的值
            checkBorderAndCenterWhenScale();
            mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
            setImageMatrix(mScaleMatrix);
            currentScale = getScale();
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            originScale = getScale();
            // 由于每次检查边界中心都是在缩放前,缩放后尺寸改变也需要检查
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
        }
    }
}

代码写的很清楚,注释也很详细,会玩的自己会去看的,不会玩的用都不会用- -。这个是类是继承自ImageView的,所以完全可以在xml中编写,而且在onTouchEvent中已经处理好了相关的事件分发,所以完全也可以用在ViewPager中不用担心冲突。

感觉一定要自己动手啊,别人的毕竟是别人的,谁说不是呢?

参考资料:
Android 手势检测实战 打造支持缩放平移的图片预览效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值