理解PhotoView的核心,以双击事件为例

原创 2016年06月02日 12:27:42

核心

对于一个ImageView或者说View来说,view的大小是不变的,那图片如何实现大小的变化呢?答案就是Matrix
Matrix的定义:字面意思是矩阵,在Android中表示使用矩阵的方法来对图片进行变换。可以略微想象一下,图片有很多个像素点,每个像素点都有它的值,将图片看作一个矩阵,那么对图片进行旋转,缩放实际上就是对矩阵进行变换。Android对这个变换过程封装进了Matrix类
在PhotoView中,ImageView(PhotoView继承于ImageView)的大小等是不变的,变化的是里面的drawable

整体结构

PhotoView:继承于ImageView 实现了IPhotoView接口。
IPhotoView接口:与PhotoView相关的接口。涉及到手势事件的监听和图像的变换。
PhotoViewAttacher:实现了IPhotoView,OnTouchListener等事件的接口。是PhotoView中接口的具体实现类

PhotoView中的几个Matrix

  • mBaseMatrix 基础的matrix,图片根据ScaleType来决定最初的大小,在初始化完成之后就基本不会改变,除非布局发生了变化。
  • mSuppMatrix 由于mBaseMatrix在初始化后基本是不变的(布局发生变化的时候会重置mBaseMatrix),因此需要另外一个matrix来记录这些变化(比如说双击事件后缩放啊什么的),然后通过mBaseMatrix的右乘来将变化加进去。
  • mDrawMatrix mBaseMatrix右乘mSuppMatrix,实际上就是存储所有变化的matrix,最后set进imageView的就是这个

具体看代码流程

PhotoView初始化的时候干了三件事:

  • 绑定PhotoViewAttacher对象
  • 根据ScaleType确定初始Matrix,也就是mBaseMatrix。这里注意,进行Matrix变换必须将ScaleType设置为Matix。那表示我们设置ScaleType就没有效果了吗?其实,PhotoView在初始化时会先记录下当前设置的ScaleType,只有在需要进行变换的时候才会将ScaleType设置成Matrix。
  • 设置各种监听,Drag,手势,DoubleClick等。

一个手势的具体运作流程,以双击事件为例

添加监听通常是在初始化时完成的,去PhotoView和PhotoViewAttacher的构造函数里找找看。最终在PhotoViewAttacher的构造函数中找到了:

mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));

mGestureDetector负责从onTouch中把MotionEvent传递给DefaultOnDoubleTapListener。

if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
                handled = true;
}

总而言之,DefaultOnDoubleTapListener是通过如上的方式接收到View的MotionEvent的,但这不是重点,重点是在DefaultOnDoubleTapListener中如何处理这个事件。代码其实很少:

 @Override
    public boolean onDoubleTap(MotionEvent ev) {
        if (photoViewAttacher == null)
            return false;

        try {
            float scale = photoViewAttacher.getScale();//获取当前缩放值
            float x = ev.getX();
            float y = ev.getY();
            //PhotoView预定义了3种缩放大小,大,中,小,可以实际使用感受下,双击后先缩放到中,再双击缩放到大,再双击缩放到小。
            if (scale < photoViewAttacher.getMediumScale()) {//当前缩放为小,变换为中
             photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true);
            } else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) {//当前缩放为中,变化为大
                photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true);
            } else {//当前缩放为大,变换为小
                photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            // Can sometimes happen when getX() and getY() is called
        }

        return true;
    }

不管是当前是哪种缩放大小,都调用了PhotoViewAttacher的setScale方法,看看这个方法:

@Override
    public void setScale(float scale, float focalX, float focalY,
                         boolean animate) {
        ImageView imageView = getImageView();

    if (null != imageView) {
        // Check to see if the scale is within bounds
        if (scale < mMinScale || scale > mMaxScale) {
            return;
        }

        if (animate) {//双击缩放都是有动画的,所以调用的是这个方法
            imageView.post(new AnimatedZoomRunnable(getScale(), scale,
                    focalX, focalY));
//动画的具体实现是通过不停地调用一个runnable,直到到达动画的duration,具体实现可以自己去看下。
        } else {
    //没有缩放动画就直接setScale
            mSuppMatrix.setScale(scale, scale, focalX, focalY);
            checkAndDisplayMatrix();//这个方法很关键,如果你自己写过双击缩放,会发现有时缩放后会把View的背景露出来,也就是drawable没有贴合在view的边缘
        }
    }
}


private void checkAndDisplayMatrix() {
    if (checkMatrixBounds()) {//这个就是我们说的关键方法
        setImageViewMatrix(getDrawMatrix());//这里就是把mDrawMatrix设置进PhotoView中
    }
}



/**
 * 对drawable进行平移使其贴合边界
 *
 * @return
 */
private boolean checkMatrixBounds() {
    final ImageView imageView = getImageView();
    if (null == imageView) {
        return false;
    }

    //这个方法获取在matrix变换后的drawable矩阵
    final RectF rect = getDisplayRect(getDrawMatrix());
    if (null == rect) {
        return false;
    }

    //以上边界不贴合为例,下方注释的地方为关键处
    final float height = rect.height(), width = rect.width();
    float deltaX = 0, deltaY = 0;

    final int viewHeight = getImageViewHeight(imageView);
    if (height <= viewHeight) {
        switch (mScaleType) {
            case FIT_START:
                deltaY = -rect.top;
                break;
            case FIT_END:
                deltaY = viewHeight - height - rect.top;
                break;
            default:
                deltaY = (viewHeight - height) / 2 - rect.top;
                break;
        }
    } else if (rect.top > 0) {//贴合的top应该是小于等于0的
        deltaY = -rect.top;//位移量
    } else if (rect.bottom < viewHeight) {//下边缘不贴合
        deltaY = viewHeight - rect.bottom;
    }

    final int viewWidth = getImageViewWidth(imageView);
    if (width <= viewWidth) {
        switch (mScaleType) {
            case FIT_START:
                deltaX = -rect.left;
                break;
            case FIT_END:
                deltaX = viewWidth - width - rect.left;
                break;
            default:
                deltaX = (viewWidth - width) / 2 - rect.left;
                break;
        }
        mScrollEdge = EDGE_BOTH;
    } else if (rect.left > 0) {
        mScrollEdge = EDGE_LEFT;
        deltaX = -rect.left;
    } else if (rect.right < viewWidth) {
        deltaX = viewWidth - rect.right;
        mScrollEdge = EDGE_RIGHT;
    } else {
        mScrollEdge = EDGE_NONE;
    }

    // 根据计算出的delta去进行平移,这边的逻辑还是比较简单的
    mSuppMatrix.postTranslate(deltaX, deltaY);
    return true;
}

至此,一次双击事件的处理就完成了,其他的手势操作都类似。PhotoView的核心是Matrix,如果把其中Matrix的变化过程和对细节的处理理解了,那么手势事件的处理就游刃有余了。

PhotoView源码解析

PhotoView的点击事件遇到的问题

问题描述 功能实现 总结问题描述项目中有个查看图片的功能,之前的同事用的PhotoView + ViewPager 实现的,现在我要补充的一个功能就是点击其中一张图片然后查看原图(带转场动画),再点击...
  • u013268154
  • u013268154
  • 2016年03月29日 14:36
  • 4773

PhotoView点击事件

PhotoView是一个很棒的Android开源组件,可以伸缩图片,双击放大,单击返回。 这里主要实现一个单击返回: photoView.setOnPhotoTapListener(new OnP...
  • luoxiping1
  • luoxiping1
  • 2015年12月13日 10:21
  • 3053

PhotoView单击退出 点击事件无效解决方法

photoView是个很好的工具,相信很多人都用过,现在项目中需要单击图片退出,于是就果断给图片加了个点击事件: imageView.setOnClickListener(new View.OnCl...
  • lvkaixuan
  • lvkaixuan
  • 2017年04月26日 11:47
  • 1279

安卓使用PhotoView进行对图片的双击放大,单击退出

实现思路 1.复制PhotoView 到libs下,然后进行添加小奶瓶 2.布局xml文件,添加PhotoView控件,src加载一张图片,就已经实现了放大缩小 3.Photoview设置点击...
  • lisiruxiaoniu
  • lisiruxiaoniu
  • 2017年09月06日 09:30
  • 389

photoview实现图片左右滑动 放大缩小 根据手势监听

  • 2015年10月14日 09:25
  • 2.73MB
  • 下载

android photoview 图片放大缩放功能 ImageView

android 图片浏览功能 图片放大缩小 使用 photoview 双击或双指缩放的ImageView 使用多点触控和双击。 滚动,以平滑滚动甩。 github 下载地址: https...
  • aaawqqq
  • aaawqqq
  • 2015年01月25日 23:49
  • 52968

PhotoView开源项目剖析

介绍 上一节呢,我们介绍了怎么下载和编译Android源码,这节呢,我们来讨论PhotoView这个开源项目,也是我们用的非常频繁的一个,用来帮助产生一个容易实现ImageView缩放的这么个东东。 ...
  • wu928320442
  • wu928320442
  • 2015年01月23日 15:43
  • 42535

开源库PhotoView修改二级双击放大

[TOC] 图片缩放 https://github.com/chrisbanes/PhotoView 修改双击直接放到最大,取消二次双击 缩放异常处理 修改双击直接放到最大,取消二次双击找到...
  • u011615817
  • u011615817
  • 2016年03月10日 14:16
  • 893

Android中View的双击,多击实现方法

一、第一种双击方式:在这里用到Android开发者最青睐的一种注入框架ButterKnife来写了一个点击事件,他可以节省很多的代码,后面可能会讲,此处不再赘述!我们知道,android有一个Syst...
  • wbq620524
  • wbq620524
  • 2016年11月04日 11:46
  • 507

理解PhotoView的核心,以双击事件为例

核心对于一个ImageView或者说View来说,view的大小是不变的,那图片如何实现大小的变化呢?答案就是Matrix。 Matrix的定义:字面意思是矩阵,在Android中表示使用矩阵的方法...
  • yi_shun
  • yi_shun
  • 2016年06月02日 12:27
  • 857
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:理解PhotoView的核心,以双击事件为例
举报原因:
原因补充:

(最多只允许输入30个字)