功能

初始时大小控制,图片宽或高大于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_FULL, SCALE_DOUBLE, SCALE_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 mLastX, mLastY;
    private boolean isCanDrag;
    private int lastPointerCount;
    private boolean isCheckTopAndBottom = true, isCheckLeftAndRight = 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_INIT, SCALE_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 x, y;//缩放的中心
        /**传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小 */
        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(tmpScale, tmpScale, x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
            // 如果值在合法范围内,继续缩放
            if ((tmpScale > 1f && getScale() < mTargetScale) || (tmpScale < 1f && getScale() > mTargetScale)) {
                post(this);
            } else { // 设置为目标的缩放比例
                float deltaScale = mTargetScale / getScale();
                mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;
            }
        }
    }

}

附件列表

自定义ImageView 手势 缩放 滑动 矩阵的更多相关文章

  1. Android 自定义ImageView支持缩放,拖拽,方便复用

    今天刚发了一篇关于ImageView的缩放和拖拽的博客,然后我想了下,将他自定义下,方便我们来复用这个imageView,效果我就不多说了,http://blog.csdn.net/xiaanming ...

  2. 自定义ImageView实现图片手势滑动,多点触摸放大缩小效果

    首先呢,还是一贯作风,我们先来看看众多应用中的示例:(这种效果是很常见的,可以说应用的必须品.)                搜狐客户端                               ...

  3. (一)自定义ImageView,初步实现多点触控、自由缩放

    真心佩服那些一直专注于技术共享的大神们,正是因为他们无私的分享精神,我才能每天都有进步.近日又算是仔细学了android的自定义控件技术,跟着大神的脚步实现了一个自定义的ImageView.里面涉及到 ...

  4. js实现移动端图片预览:手势缩放, 手势拖动,双击放大...

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  5. 动画--问题追踪:ImageView执行缩放动画ScaleAnimation之后,图像显示不全的问题。

    http://www.bkjia.com/Androidjc/929473.html: 问题追踪:ImageView执行缩放动画ScaleAnimation之后,图像显示不全的问题., 问题:我有一个 ...

  6. Android自定义ImageView实现图片圆形 ,椭圆和矩形圆角显示

    Android中的ImageView只能显示矩形的图片,为了用户体验更多,Android实现圆角矩形,圆形或者椭圆等图形,一般通过自定义ImageView来实现,首先获取到图片的Bitmap,然后通过 ...

  7. android131 360 05 手势触摸滑动,sim卡,开机启动的广播,手机联系人,SharedPreferences,拦截短信

    安卓手势触摸滑动: package com.itheima52.mobilesafe.activity; import android.app.Activity; import android.con ...

  8. 缩放系列(三):一个可以手势缩放、拖拽、旋转的layout

    弄了一个下午,终于搞出来了,PowerfulLayout 下面是一个功能强大的改造的例子: 可以实现以下需求: 1.两个手指进行缩放布局 2.所有子控件也随着缩放, 3.子控件该有的功能不能丢失(像b ...

  9. mui中图片手势缩放功能的实现

    MUI框架,要实现手势缩放图片,可以使用imageviewer组件来实现.代码很简单: 引入css: <link href="assets/css/mui.imageviewer.cs ...

随机推荐

  1. [hdu3934] 凸包 旋转卡壳

    大致题意: 求多边形的最大内接三角形 旋转卡壳 模板题 #include<cstdio> #include<iostream> #include<cstring> ...

  2. IntelliJ 、Pycharm、webstorm 2017 注册码及注册服务器

    jetbrains 家的东西都非常好看,但是价格贵的令人发指,所以我搭建了一个 Pycharm激活服务器,可以用来激活 Pycharm,IntelliJ IDEA,WebStorm.避免频繁更换激活码 ...

  3. 阿里云下Linux服务器安装JDK、Tomcat

    阿里云服务器相信大家越来越熟悉,刚开始接触,将基本的java软件安装做点记录: 1.配置阿里云的yum仓库: 获取仓库配置 wget http://mirrors.aliyun.com/repo/Ce ...

  4. 深入理解javascript函数系列第一篇

    前面的话 函数对任何一门语言来说都是核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.在javascript里,函数即对象,程序可以随意操控它们.函数可以嵌套在其他函数中 ...

  5. luoguP3714 [BJOI2017]树的难题 点分治

    以后传数组绝对用指针... 考虑点分治 在点分的时候,把相同的颜色的在一起合并 之后,把不同颜色依次合并 我们可以用单调队列做到单次合并$O(n + m)$ 如果我们按照深度大小来合并,那么由于每次都 ...

  6. MSSQL SELECT(刚刚)新插入到表中的那条记录

    假设对表 TXxxxxxxx 表新插入一条记录,然后要 SELECT 出刚刚插入的这条记录.可使用 SCOPE_IDENEITY(); 处理.具体代码参考如下: INSERT INTO TXxxxxx ...

  7. BZOJ 2818: Gcd 筛法

    2818: Gcd 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2818 Description 给定整数N,求1<=x,y< ...

  8. mongoDB系列之(一):10分钟玩转mongoDB

    1. mongoDB是什麽 mongodb是时下流行的NoSql数据库,它的存储方式是文档式存储,并不是Key-Value形式. 存储在集合中的文档,被存储为键-值对的形式.键用于唯一标识一个文档,为 ...

  9. svn 服务器搭建及使用 三

    SVN服务器搭建和使用(三) 接下来,试试用TortoiseSVN修改文件,添加文件,删除文件,以及如何解决冲突等. 添加文件 在检出的工作副本中添加一个Readme.txt文本文件,这时候这个文本文 ...

  10. 微信小程序导航栏,下面内容滑动,上册导航栏跟着滑动,内容随着导航栏滑动

    16.类似微信导航栏滑动.png 今日头条导航栏,下面滑动上面跟着滑动 index.wxml <swiper class="content" style="heig ...