理解PhotoView的核心,以双击事件为例
来源:互联网 发布:java随机数生成扑克牌 编辑:程序博客网 时间:2024/06/06 19:19
核心
对于一个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的核心,以双击事件为例
- 为TableView的行实现双击事件
- 以测试用例为核心的软件测试
- 以测试用例为核心的软件测试
- 以测试用例为核心的软件测试
- 以汇编语言为核心的MD5算法
- 建设以需求为核心的知识库
- 以ADO为核心的知识讲解
- 以用户需求为核心的SEO
- control事件的重写,以一个ListMOve为例
- 以JTextPanel为例Swing的鼠标事件详解
- 接口回调,以Android的事件监听为例
- 如何让CRM的核心为:以客户为中心
- PhotoView的点击事件遇到的问题
- WPF为ListBox添加ListBoxItem的双击事件
- Cocos2d-iphone 为sprite添加双击的事件响应
- 我所理解的学习--以计算机为例
- 我所理解的研究--以计算机为例
- C++作业7
- 乐学成语实现之一:创建数据库和表
- iOS 修改状态栏颜色
- 四舍五入保留两位小数
- CentOS 安装配置vncserver
- 理解PhotoView的核心,以双击事件为例
- 删除字符串
- 启动系统设置页面
- NSInvocation 注意事项
- volatile unsigned char; volatile有利于全局变量的实时更新,尤其含有中断中改变的量
- 用VirtualBox 导入vmware的虚拟机
- PPT演示文稿放映时会议记录的技巧
- Android中<meta-data>的使用
- 设置系统栏可见性