转载请注明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_)
开源库地址:https://github.com/chrisbanes/PhotoView
PhotoView是一个用来帮助开发者轻松实现ImageView缩放的库。开发者可以轻易控制对图片的缩放旋等等操作。
PhotoView的使用极其简单,而且提供了两种方案。可以使用普通的ImageView,也可以使用该库中提供的ImageView(PhotoView)。
- 使用PhotoView
只需如下引用该库中的ImageView,无需关心其它实现细节,你的ImageView便可拥有缩放效果。
<uk.co.senab.photoview.PhotoView
android:id="@+id/iv_photo"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
- 针对普通ImageView
有的时候,可能因为一些历史原因,使得你不得不用原来的ImageView。幸运的是该库也提供了一种解决方案。只需用PhotoViewAttacher包装即可。
PhotoViewAttacher mAttacher=new PhotoViewAttacher(mImageView);//用PhotoViewAttacher包装
mAttacher.update();//当图片改变时需调用update();
mAttacher.cleanup();//当ImageView不再使用时回收资源(可在onDestory中 调用)。PhotoView已经实现了这个功能不需要自己管理。
PhotoView真的很神奇,接下来我们去源码里一探究竟吧。顺便多说一句,图片的缩放大量运用到了Matrix相关知识,不了解的务必要先查阅相关资料哦。强烈推荐Android Matrix 这篇文章,当然也可以看我的这篇Android Matrix矩阵详解。
源码解读
这次源码解读我们从使用普通ImageView入手,普通的ImageView如果想缩放,必须依赖于PhotoViewAttacher,而PhotoViewAttacher又实现了IPhotoView接口。IPhotoView主要定义了一些常用的操作和默认值,由于方法实在太多了,就不一一列举了,直接上图。
IPhotoView定义的所有抽象方法如下。
IPhotoView的部分源码如下。
public interface IPhotoView {
float DEFAULT_MAX_SCALE = 3.0f;//默认最大缩放倍数为3倍
float DEFAULT_MID_SCALE = 1.75f;//默认中间缩放倍数为1.75倍
float DEFAULT_MIN_SCALE = 1.0f;//默认最小缩放倍数为1倍
int DEFAULT_ZOOM_DURATION = 200;//默认的缩放间隔为200ms
boolean canZoom();//可以缩放
RectF getDisplayRect();//获取显示矩形
boolean setDisplayMatrix(Matrix finalMatrix);//设置显示矩阵
Matrix getDisplayMatrix();//获取显示矩阵
//..
//省略了部分源码
介绍完IPhotoView接口后,现在改来看看PhotoViewAttacher了,PhotoViewAttacher的属性也比较多,如下:
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();//插值器,用于缩放动画
int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;//默认的缩放间隔
static final int EDGE_NONE = -1;//图片两边都不在边缘内
static final int EDGE_LEFT = 0;//图片左边显示在View的左边缘内
static final int EDGE_RIGHT = 1;//图片右边显示在View的右边缘内
static final int EDGE_BOTH = 2;//图片两边都在边缘内
static int SINGLE_TOUCH = 1;//单指
private float mMinScale = DEFAULT_MIN_SCALE;//最小缩放倍数
private float mMidScale = DEFAULT_MID_SCALE;//中间缩放倍数
private float mMaxScale = DEFAULT_MAX_SCALE;//最大缩放倍数
private boolean mAllowParentInterceptOnEdge = true;//当在边缘操作时,允许父布局拦截事件。
private boolean mBlockParentIntercept = false;//阻止父布局拦截事件
private WeakReference<ImageView> mImageView;//弱引用
//手势探测器
private GestureDetector mGestureDetector;//单击,长按,Fling
private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector;//缩放和拖拽
private final Matrix mBaseMatrix = new Matrix();//基础矩阵,用来保存初始的显示矩阵
private final Matrix mDrawMatrix = new Matrix();//绘画矩阵,用来计算最后显示区域的矩阵,是在mBaseMatrix和mSuppMatrix的基础上计算出来的。
private final Matrix mSuppMatrix = new Matrix();//这个矩阵我也不知道怎么称呼,也不知道是不是Supply的意思,暂且叫作供应矩阵吧,用来保存旋转平移和缩放的矩阵。
private final RectF mDisplayRect = new RectF();//显示矩形
private final float[] mMatrixValues = new float[9];//用来保存矩阵的值。3*3
// 各类监听
private OnMatrixChangedListener mMatrixChangeListener;
private OnPhotoTapListener mPhotoTapListener;
private OnViewTapListener mViewTapListener;
private OnLongClickListener mLongClickListener;
private OnScaleChangeListener mScaleChangeListener;
private OnSingleFlingListener mSingleFlingListener;
//保存ImageView的top,right,bottom,left
private int mIvTop, mIvRight, mIvBottom, mIvLeft;
//Fling时的Runable
private FlingRunnable mCurrentFlingRunnable;
private int mScrollEdge = EDGE_BOTH;//两边边缘
private float mBaseRotation;//基础旋转角度
private boolean mZoomEnabled;//是否可以缩放
private ScaleType mScaleType = ScaleType.FIT_CENTER;//默认缩放类型
此外PhotoViewAttacher中还定义了以下几个接口。
public interface OnMatrixChangedListener {
/**
* 当用来显示Drawable的Matrix改变时回调
* @param rect - 显示Drawable的新边界
*/
void onMatrixChanged(RectF rect);
}
public interface OnScaleChangeListener {
/**
* 当ImageView改变缩放时回调
*
* @param scaleFactor 小于1表示缩小,大于1表示放大
* @param focusX 缩放焦点X
* @param focusY 缩放焦点Y
*/
void onScaleChange(float scaleFactor, float focusX, float focusY);
}
public interface OnPhotoTapListener {
/**
*
*当用户敲击在照片上时回调,如果在空白区域不会回调
* @param view - ImageView
* @param x -用户敲击的位置(在图片中从左往右的位置)占图片宽度的百分比
* @param y -用户敲击的位置(在图片中从上往下的位置)占图片高度的百分比
*/
void onPhotoTap(View view, float x, float y);
/**
* 在图片外部的空白区域敲击回调
* */
void onOutsidePhotoTap();
}
public interface OnViewTapListener {
/**
* 只要用户敲击ImageView就会回调,不管是不是在图片上。
* @param view - View the user tapped.
* @param x -敲击View的x坐标
* @param y -敲击View的y坐标
*/
void onViewTap(View view, float x, float y);
}
public interface OnSingleFlingListener {
/**
* 用户使用单指在ImageView上快速滑动时回调,不管是不是在图片上。
* @param e1 - 第一次触摸事件
* @param e2 - 第二次触摸事件
* @param velocityX - 水平滑过的速度.
* @param velocityY - 竖直滑过的素组.
*/
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
在看完PhotoViewAttacher的一些属性和接口外,现在就来看PhotoViewAttacher的构造方法。即new PhotoViewAttacher(mImageView)
这一句。
public PhotoViewAttacher(ImageView imageView) {
this(imageView, true);
}
public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
mImageView = new WeakReference<>(imageView);//弱引用
imageView.setDrawingCacheEnabled(true);//开启绘制缓存区,用于获取可见区的bitmap
imageView.setOnTouchListener(this);//设置Touch监听,用于添加手势监听
ViewTreeObserver observer = imageView.getViewTreeObserver();
if (null != observer)
observer.addOnGlobalLayoutListener(this);//用于监听ImageView的大小
// 确保ImageView的ScaleType为Matrix
setImageViewScaleTypeMatrix(imageView);
if (imageView.isInEditMode()) {
return;
}
//初始化多指缩放/拖拽手势探测器
mScaleDragDetector = VersionedGestureDetector.newInstance(
imageView.getContext(), this);
//初始化其它手势监听(长按,Fling)
mGestureDetector = new GestureDetector(imageView.getContext(),
new GestureDetector.SimpleOnGestureListener() {
//长按
@Override
public void onLongPress(MotionEvent e) {
if (null != mLongClickListener) {
mLongClickListener.onLongClick(getImageView());
}
}