PinchImageView
使用 GestureDetector
来处理长按、点击、双击、惯性滑动事件,在 onTouchEvent
里处理双指缩放和单指移动等事件。 里面有两个矩阵,一个是外部变换矩阵(mOuterMatrix),主要记录手势操作的结果,一个是内部变换矩阵(getInnerMatrix(Matrix)),就是根据 fitCenter
等缩放模式进行缩放平移后的初始矩阵。这里区分两个矩阵可能是借鉴了PhotoView的经验,手势操作和原始缩放互不影响,手势操作后最终的缩放只需要两个矩阵相乘就好了。 下面的代码分析不一定会完全贴源码,有的是经过稍微改动的。
1 双击、惯性滑动
长按和点击就是调用回调,我们主要来看双击和惯性滑动。
1.1 双击
PinchImageView
只做了一级的放大缩小 ,就是说只能在最大和初始缩放值之间切换。 基本原理:捕获双击事件,拿到双击点的x、y坐标,对图片进行缩放变换,将双击点位置移动到视图中间。 代码较长,我们一点点拆分。 这里要先介绍下 PinchImageView
的对象池(ObjectsPool)。 ObjectsPool
维护一个对象队列,在容量范围内可以循环复用对象。大致使用流程如下图所示:
- 在队列里获取
innerMatrix
对象(take()),队列为空则新建一个对象返回,否则出队一个对象重置后返回。 - 在队列里获取
targetMatrix
对象。 - 使用完
targetMatrix
归还(given(obj))。 - 使用完
innerMatrix
归还。
归还顺序无所谓。
/**
* 对象池
*
* 防止频繁new对象产生内存抖动.
* 由于对象池最大长度限制,如果吞吐量超过对象池容量,仍然会发生抖动.
* 此时需要增大对象池容量,但是会占用更多内存.
*
* @param <T> 对象池容纳的对象类型
*/
private static abstract class ObjectsPool<T> {
/**
* 对象池的最大容量
*/
private int mSize;
/**
* 对象池队列
*/
private Queue<T> mQueue;
public ObjectsPool(int size) {
mSize = size;
mQueue = new LinkedList<T>();
}
public T take() {
//如果池内为空就创建一个
if (mQueue.size() == 0) {
return newInstance();
} else {
//对象池里有就从顶端拿出来一个返回
return resetInstance(mQueue.poll());
}
}
public void given(T obj) {
//如果对象池还有空位子就归还对象
if (obj != null && mQueue.size() < mSize) {
mQueue.offer(obj);
}
}
abstract protected T newInstance();
abstract protected T resetInstance(T obj);
}
继续看双击事件的处理。
private void doubleTap(float x, float y) {
//获取第一层变换矩阵
Matrix innerMatrix = MathUtils.matrixTake();
getInnerMatrix(innerMatrix);
……
MathUtils.matrixGiven(innerMatrix);
}
首先是获取内部变换矩阵。MathUtils.matrixTake()
是从 Matrix
对象池(MatrixPool)里获取一个 Matrix
对象。
public static Matrix matrixTake() {
return mMatrixPool.take();
}
/**
* 获取某个矩阵的copy
*/
public static Matrix matrixTake(Matrix matrix) {
Matrix result = mMatrixPool.take();
if (m