最近写了一个Android单图片显示的Activity,基于Android 2.1+(API 7+),实现浮现放大缩小控制按钮进行大小缩放,双击放大缩小,移动,多点(两点)缩放功能。

这个东西还比较常用,我参看了Android源码中的Gallery和Gallery3D,主要是Gallery,这个可以实现“浮现放大缩小控制 按钮进行大小缩放,双击放大缩小,移动”,但是不支持多点缩放,而且这个是基于列表显示的,有很多无用的代码。(这两个应用单独的源码可以通过git clone https://android.googlesource.com/platform/packages/apps/Gallery.git和https://android.googlesource.com/platform/packages/apps/Gallery3D下载)

应用效果观察了:快图浏览,Gallery3D,Gallery,MIUI图库4款应用,主要体会了多点缩放。快图浏览和MIUI图库缩放效果类似,Gallery3D多点缩放仔细斟酌起来,感觉有点问题,会产生诡异的偏移。

先看一下Gallery的源码,由两个package组成,分别是com.android.camera和 com.android.camera.gallery。com.android.camera.gallery主要包含了封装的一些Image类,层次 结构在IImageList中给出了:

 // ImageList and Image classes have one-to-one correspondence.
// The class hierarchy (* = abstract class):
//
// IImageList
// - BaseImageList (*)
// - VideoList
// - ImageList
// - DrmImageList
// - SingleImageList (contains UriImage)
// - ImageListUber
//
// IImage
// - BaseImage (*)
// - VideoObject
// - Image
// - DrmImage
// - UriImage
//

还包含了一个LruCache,Least Recently Used缓存算法的一个实现。

重点是com.android.camera包,主要涉及到ViewImage,ImageViewTouchBase,RotateBitmap,ImageGetter等类。

ImageViewTouchBase类是一个抽象类,继承自ImageView,实现了放大缩小zoom,平移pan等方法。主要用到Matrix类,起初我还不是很明白这个是个什么东西,看了文档也不清楚。官方文档如下:

The Matrix class holds a 3x3 matrix for transforming coordinates. Matrix does not have a constructor, so it must be explicitly initialized using either reset() - to construct an identity matrix, or one of the set..() functions (e.g. setTranslate, setRotate, etc.).

后来看了一段时间源代码,突然想起了图形学中的变换矩阵,瞬间明白了这个东西就是图形的变换矩阵啊。

变换矩阵可以参看维基百科内容http://zh.wikipedia.org/wiki/%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5#.E5.9C.A8.E4.BA.8C.E7.BB.B4.E5.9B.BE.E5.BD.A2.E4.B8.AD.E7.9A.84.E5.BA.94.E7.94.A8.E7.A4.BA.E4.BE.8B

ImageViewTouchBase用到了3个Matrix,分别是 mBaseMatrix,mSuppMatrix,mDisplayMatrix。其中mBaseMatrix用于显示原始image的基础变换矩 阵,mSuppMatrix存储用户对图形产生的缩放平移变换的外加的矩阵。mDisplayMatrix是基本矩阵加上额外的变换矩阵产生的最终的矩 阵。

然后剩下的一些放大缩小平移的方法,就是改变mSuppMatrix的值,应用缩放平移矩阵变换,然后算出最终的mDisplayMatrix。然 后调用父类ImageView的setImageMatrix方法,应用矩阵变换。(ImageViewTouchBase的ScaleType已经被设 定为MATRIX)

然后ViewImage是用于显示图片的这个Activity,里面大多是处理交互的逻辑,处理双击缩放,平移,缩放按钮的显示隐藏等。缩放按钮 Android提供了ZoomButtonsController,这个是继承自Object类的,不是一个View,构造的时候需要传入一个 ownerView,构造函数是:ZoomButtonsController(View ownerView)

ZoomButtonsController的实现按钮点击的机制有点坑,它捕获了ownerView的onTouch事件,所以当ZoomButtonsController可见后,我在处理ownerView的事件时,发现都不会触发了,纠结了很久。

然后稍加修正一个可以放大缩小平移的用于显示图片的Activity就成型了。然后就是添加多点缩放功能,Gallery没有提供多点缩放的功能。 Gallery3D有多点缩放,但是使用Surface实现的,但是还是有一些帮助,翻了代码找到ScaleGestureDetector类,发现 Android在API Level 8引入了这个类,但是看了源代码发现,里面实现的细节还是有很多出入的。我把API中的ScaleGestureDetector抠了出来,可以用。实现 多点缩放的话,IamgeViewTouchBash类中的一些方法可以用,缩放方法是一样的,就是计算缩放中心点和缩放大小,Android自API Level 5引入了多点的API,也就是说Android 1.6(即API 4)及以下没法多点缩放了。ScaleGestureDetector没有提供两点中点坐标的方法,但是可以轻松添加,提供了缩放大小方法。还涉及到一些 小细节的改变,然后让Activity的rootLayout监听onTouch事件。有一个小bug,就是多点变一点后有可能触发平移。看 Gallery3D代码也是用GestureDetector和ScaleGestureDetector来实现的,而且又重载了onTouch事件处理 函数,感觉有点乱。我只用了GestureDetector和ScaleGestureDetector,也有点乱,这个稍后要重构一下。

现在基本能用,代码在github上,地址:https://github.com/qhm123/SimpleTouchImageView (后来又加了写别的东西,单个图片查看Activity的地址是https://github.com/qhm123/SimpleTouchImageView/tree/v_feedback_fixed)

显示图片的Activity是TouchImageActivity,传入图片地址url参数即可。

考虑到的后期优化:
增加ImageGetter缩略图效果。
增加放大缩小平移超出范围时的回放效果。(已加)
增加多点旋转。

过程中找到一篇Blog《Touch事件派发过程详解》,感觉不错。