正好Philm项目中使用到了PhotoView,借此机会深入了解PhotoView的源码,PhotoView是一个非常好的图片显示第三方开源库,支持手势缩放移动图片等操作。PhotoView源码可在github上下载,下载链接PhotoView

重要的类和字段功能

1
2
3
4
5
6
7
8
9
10
11
12
13
PhotoView extends ImageView
内部强制将ImageView的scaletype保持为matrix,在xml中设置不生效,可以通过设置setScaleType,修改PhotoViewAttacher的scaleType,从而达到需要的效果
PhotoViewAttacher:PhoneView的几乎所有功能实现类
mBaseRotation:图片默认旋转角,mSuppMatrix会默认postRotation(mBaseRotation)
mBaseMatrix :根据imaegview scaleType来生成,即控制drawable instinct width/height到imageview width/height的矩阵,
生成baseMatrix的代码非常有意思,下面将baseMatrix的生成代码贴了出来
mSuppMatrix:对应用户手势变化的矩阵
mDrawMatrix:mSuppMatrix*mBaseMatrix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//BaseMatrix举证的生成代码
private void updateBaseMatrix(Drawable d) {
ImageView imageView = getImageView();
if (null == imageView || null == d) {
return;
}
final float viewWidth = getImageViewWidth(imageView);
final float viewHeight = getImageViewHeight(imageView);
final int drawableWidth = d.getIntrinsicWidth();
final int drawableHeight = d.getIntrinsicHeight();
mBaseMatrix.reset();
final float widthScale = viewWidth / drawableWidth;
final float heightScale = viewHeight / drawableHeight;
if (mScaleType == ScaleType.CENTER) {
mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
(viewHeight - drawableHeight) / 2F);
} else if (mScaleType == ScaleType.CENTER_CROP) {
float scale = Math.max(widthScale, heightScale);
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else if (mScaleType == ScaleType.CENTER_INSIDE) {
float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else {
RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
if ((int) mBaseRotation % 180 != 0) {
mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
}
switch (mScaleType) {
case FIT_CENTER:
mBaseMatrix
.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
break;
case FIT_START:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
break;
case FIT_END:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
break;
case FIT_XY:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
break;
default:
break;
}
}
resetMatrix();
}

学习知识点

onTouch ACTION_DOWN时

通过parent.requestDisallowInterceptTouchEvent(true)阻止parent劫持事件

matrix的scale获取方法:

1
(float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));

GestureDetector.OnDoubleTapListener

通过该listener可以监听onSingleTapConfirmed, onDoubleTap

GestureDetector.SimpleOnGestureListener

通过该listener可以监听onLongPress, onFling

ScaleGestureDetector.OnScaleGestureListener

通过该listener可以监听onScale,onScaleBegin,onScaleEnd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
return false;
mListener.onScale(scaleFactor,
detector.getFocusX(), detector.getFocusY());
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// NO-OP
}
};
mDetector = new ScaleGestureDetector(context, mScaleListener);

VelocityTracker

通过VelocityTracker可以检测fling的速度,重要接口如下:

  • mVelocityTracker.addMovement(ev);
  • mVelocityTracker.recycle()
  • mVelocityTracker.computeCurrentVelocity(1000);
  • mVelocityTracker.getXVelocity()
  • mVelocityTracker.getXVelocity()

OverScroller

通过OverScroller可以控制fling,重要接口如下:

  • mScroller.computeScrollOffset()
  • mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY)
  • mScroller.isFinished()
  • mScroller.getCurrX()
  • mScroller.getCurrY()