PhotoView源码分析(2)
2015-09-04 22:56
495 查看
1 PhotoViewAttacher
代码较多,省去不重要的public class PhotoViewAttacher implements IPhotoView, View.OnTouchListener, OnGestureListener, ViewTreeObserver.OnGlobalLayoutListener { // 变量定义及初始化 static final Interpolator sInterpolator = new AccelerateDecelerateInterpolator(); int ZOOM_DURATION = DEFAULT_ZOOM_DURATION; static final int EDGE_NONE = -1; static final int EDGE_LEFT = 0; static final int EDGE_RIGHT = 1; static final int EDGE_BOTH = 2; 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; /** * @return true if the ImageView exists, and it's Drawable existss */ private static boolean hasDrawable(ImageView imageView) { return null != imageView && null != imageView.getDrawable(); } private WeakReference<ImageView> mImageView; // Gesture Detectors private GestureDetector mGestureDetector; // 单机双击等 private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector; // 算法 // These are set so we don't keep allocating them on the heap private final Matrix mBaseMatrix = new Matrix(); // 初始化后基本不变了 private final Matrix mDrawMatrix = new Matrix(); // 最终变换使用的,由上下两个合成而来 private final Matrix mSuppMatrix = new Matrix(); // 存储需要变换的值 private final RectF mDisplayRect = new RectF(); private final float[] mMatrixValues = new float[9]; // Listeners private OnMatrixChangedListener mMatrixChangeListener; private OnPhotoTapListener mPhotoTapListener; // 点击到了图片上 private OnViewTapListener mViewTapListener; // 点击到了view上 private OnLongClickListener mLongClickListener; private OnScaleChangeListener mScaleChangeListener; private int mIvTop, mIvRight, mIvBottom, mIvLeft; private FlingRunnable mCurrentFlingRunnable; // Fling private int mScrollEdge = EDGE_BOTH; // 边界类型 private boolean mZoomEnabled; private ScaleType mScaleType = ScaleType.FIT_CENTER; // 默认的类型!!! public PhotoViewAttacher(ImageView imageView) { this(imageView, true); } @SuppressLint("NewApi") public PhotoViewAttacher(ImageView imageView, boolean zoomable) { mImageView = new WeakReference<ImageView>(imageView); //store // true to enable the drawing cache, false otherwise imageView.setDrawingCacheEnabled(true); // 后面要用cache imageView.setOnTouchListener(this); ViewTreeObserver observer = imageView.getViewTreeObserver(); if (null != observer) observer.addOnGlobalLayoutListener(this); // Make sure we using MATRIX Scale Type setImageViewScaleTypeMatrix(imageView); // True if this View is in edit mode, false otherwise. if (imageView.isInEditMode()) { return; } // Create Gesture Detectors... // 其实只是一个计算方法的集合,回调到这,和GestureDetector不一样,GestureDetector是系统提供的 mScaleDragDetector = VersionedGestureDetector.newInstance( imageView.getContext(), this); mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() { // forward long click listener @Override public void onLongPress(MotionEvent e) { // 长按事件 if (null != mLongClickListener) { mLongClickListener.onLongClick(getImageView()); } } }); // 设置另一个监听 mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this)); // Finally, update the UI so that we're zoomable setZoomable(zoomable); } /** * Clean-up the resources attached to this object. This needs to be called when the ImageView is * no longer used. A good example is from {@link android.view.View#onDetachedFromWindow()} or * from {@link android.app.Activity#onDestroy()}. This is automatically called if you are using * {@link uk.co.senab.photoview.PhotoView}. */ @SuppressWarnings("deprecation") public void cleanup() { // 最好在onDetachedFromWindow()或onDestroy()中调用这个方法 // 而PhotoView已经自动完成了 // 清空所有的回调,变量也置空 if (null == mImageView) { return; // cleanup already done } final ImageView imageView = mImageView.get(); if (null != imageView) { // Remove this as a global layout listener ViewTreeObserver observer = imageView.getViewTreeObserver(); if (null != observer && observer.isAlive()) { observer.removeGlobalOnLayoutListener(this); } // Remove the ImageView's reference to this imageView.setOnTouchListener(null); // make sure a pending fling runnable won't be run cancelFling(); } if (null != mGestureDetector) { mGestureDetector.setOnDoubleTapListener(null); } // Clear listeners too mMatrixChangeListener = null; mPhotoTapListener = null; mViewTapListener = null; // Finally, clear ImageView mImageView = null; } @Override public RectF getDisplayRect() { // 获得当前Imageview中内容的显示区域 checkMatrixBounds();// 检测范围并调整,不能出现空隙 return getDisplayRect(getDrawMatrix()); // 根据最新的matrix返回Rect } @Override public void setRotationTo(float degrees) { mSuppMatrix.setRotate(degrees % 360); checkAndDisplayMatrix(); } @Override public void setRotationBy(float degrees) { mSuppMatrix.postRotate(degrees % 360); checkAndDisplayMatrix(); } public ImageView getImageView() { ImageView imageView = null; if (null != mImageView) { imageView = mImageView.get(); // 从弱引用中拿取 } // If we don't have an ImageView, call cleanup() if (null == imageView) { cleanup(); } return imageView; } @Override public float getScale() { // Math.pow幂 return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2)); } // 底层回调过来的,表示手指进行了拖动 @Override public void onDrag(float dx, float dy) { if (mScaleDragDetector.isScaling()) { return; // Do not drag if we are already scaling } ImageView imageView = getImageView(); mSuppMatrix.postTranslate(dx, dy); // 变换量添加 checkAndDisplayMatrix(); // very good! good idea! /** * Here we decide whether to let the ImageView's parent to start taking * over the touch event. * * First we check whether this function is enabled. We never want the * parent to take over if we're scaling. We then check the edge we're * on, and the direction of the scroll (i.e. if we're pulling against * the edge, aka 'overscrolling', let the parent take over). */ ViewParent parent = imageView.getParent(); if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) { if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f) || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) { if (null != parent) // 这样便可以进行viewpager等的滑动了 parent.requestDisallowInterceptTouchEvent(false); // 允许阻断事件向下传递 } } else { if (null != parent) { parent.requestDisallowInterceptTouchEvent(true); // 不允许阻断,必须传给我 } } } // 底层回调过来的 @Override public void onFling(float startX, float startY, float velocityX, float velocityY) { ImageView imageView = getImageView(); mCurrentFlingRunnable = new FlingRunnable(imageView.getContext()); mCurrentFlingRunnable.fling(getImageViewWidth(imageView),// 启动scroll的Fling方法 getImageViewHeight(imageView), (int) velocityX, (int) velocityY); imageView.post(mCurrentFlingRunnable); } @Override public void onGlobalLayout() { // positon is ok ImageView imageView = getImageView(); if (null != imageView) { if (mZoomEnabled) { final int top = imageView.getTop(); final int right = imageView.getRight(); final int bottom = imageView.getBottom(); final int left = imageView.getLeft(); /** * We need to check whether the ImageView's bounds have changed. * This would be easier if we targeted API 11+ as we could just use * View.OnLayoutChangeListener. Instead we have to replicate the * work, keeping track of the ImageView's bounds and then checking * if the values change. */ if (top != mIvTop || bottom != mIvBottom || left != mIvLeft || right != mIvRight) { // Update our base matrix, as the bounds have changed updateBaseMatrix(imageView.getDrawable()); // Update values as something has changed mIvTop = top; mIvRight = right; mIvBottom = bottom; mIvLeft = left; } } else { updateBaseMatrix(imageView.getDrawable()); } } } // 底层回调过来的 @Override public void onScale(float scaleFactor, float focusX, float focusY) { if (getScale() < mMaxScale || scaleFactor < 1f) { if (null != mScaleChangeListener) { mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY); } // 添加至mSuppMatrix mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); checkAndDisplayMatrix(); // 更新matrix并应用显示 } } @Override public boolean onTouch(View v, MotionEvent ev) { boolean handled = false; // for return if (mZoomEnabled && hasDrawable((ImageView) v)) { ViewParent parent = v.getParent(); switch (ev.getAction()) { case ACTION_DOWN: // First, disable the Parent from intercepting the touch event if (null != parent) { // 重要 parent.requestDisallowInterceptTouchEvent(true); } else { LogManager.getLogger().i(LOG_TAG, "onTouch getParent() returned null"); } // If we're flinging, and the user presses down, cancel fling cancelFling(); break; case ACTION_CANCEL: case ACTION_UP: // If the user has zoomed less than min scale, zoom back to min scale if (getScale() < mMinScale) { RectF rect = getDisplayRect(); // 获得.. if (null != rect) { v.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY())); handled = true; } } break; } // Try the Scale/Drag detector if (null != mScaleDragDetector) { boolean wasScaling = mScaleDragDetector.isScaling(); boolean wasDragging = mScaleDragDetector.isDragging(); handled = mScaleDragDetector.onTouchEvent(ev); // 每次都需要传入进行计算,以便及时回调 boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling(); boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging(); mBlockParentIntercept = didntScale && didntDrag; //多重保险 } // Check to see if the user double tapped每次都需要传入进行计算,以便及时回调 if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) { handled = true; } } return handled; } @Override public void setScale(float scale, boolean animate) {// animate是否渐变 ImageView imageView = getImageView(); if (null != imageView) { setScale(scale, (imageView.getRight()) / 2, (imageView.getBottom()) / 2, animate); } } @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) { // 渐变则使用run实现 imageView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY)); } else { // 否则直接完成 mSuppMatrix.setScale(scale, scale, focalX, focalY); checkAndDisplayMatrix(); } } } public void update() { ImageView imageView = getImageView(); if (null != imageView) { if (mZoomEnabled) { // Make sure we using MATRIX Scale Type setImageViewScaleTypeMatrix(imageView); // Update the base matrix using the current drawable updateBaseMatrix(imageView.getDrawable()); } else { // Reset the Matrix... resetMatrix(); } } } // 获得当前的矩阵 public Matrix getDrawMatrix() { mDrawMatrix.set(mBaseMatrix); mDrawMatrix.postConcat(mSuppMatrix); // mDrawMatrix的设置,先进行了mBaseMatrix变换,而后进行mSuppMatrix的变换 // postTranslate原理一样吧 return mDrawMatrix; } private void cancelFling() { if (null != mCurrentFlingRunnable) { mCurrentFlingRunnable.cancelFling(); mCurrentFlingRunnable = null; } } /** * Helper method that simply checks the Matrix, and then displays the result */ private void checkAndDisplayMatrix() { // 检测并设定 if (checkMatrixBounds()) { setImageViewMatrix(getDrawMatrix()); } } private void checkImageViewScaleType() { ImageView imageView = getImageView(); /** * PhotoView's getScaleType() will just divert to this.getScaleType() so * only call if we're not attached to a PhotoView. */ if (null != imageView && !(imageView instanceof IPhotoView)) { // 必须是Matrix if (!ScaleType.MATRIX.equals(imageView.getScaleType())) { throw new IllegalStateException( "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher"); } } } // 检测范围并调整,不能出现空隙,变换了矩阵,偏移值添加至mSuppMatrix private boolean checkMatrixBounds() { final ImageView imageView = getImageView(); if (null == imageView) { return false; } // 获得显示区域的矩形范围,或Imageview的内容显示范围 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: // 居中:- rect.top可以先让其居顶,而后(viewHeight - height) / 2让其居中 deltaY = (viewHeight - height) / 2 - rect.top; break; } } else if (rect.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; } // Finally actually translate the matrix 将当前计算的偏移值添加至mSuppMatrix供以后使用 mSuppMatrix.postTranslate(deltaX, deltaY); return true; } /** * Helper method that maps the supplied Matrix to the current Drawable * * @param matrix - Matrix to map Drawable against * @return RectF - Displayed Rectangle */ private RectF getDisplayRect(Matrix matrix) {// 根据矩阵获得显示内容的范围 ImageView imageView = getImageView(); if (null != imageView) { // 图片的Drawable是恒定不变,每次通过matrix变换后,改变了显示范围等,将这个变化的结果内容显示出来而已 Drawable d = imageView.getDrawable(); if (null != d) { mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); // mDisplayRect设定为Drawable的大小范围 // Apply this matrix to the rectangle对矩形进行matrix变换 // 这样一来,变换后的矩形就和现在显示的图形,范围上是一致的了 matrix.mapRect(mDisplayRect); return mDisplayRect; } } return null; } public Bitmap getVisibleRectangleBitmap() { // 构造中设置的cache使能 ImageView imageView = getImageView(); return imageView == null ? null : imageView.getDrawingCache(); } /** * Helper method that 'unpacks' a Matrix and returns the required value * * @param matrix - Matrix to unpack * @param whichValue - Which value from Matrix.M* to return * @return float - returned value */ private float getValue(Matrix matrix, int whichValue) { matrix.getValues(mMatrixValues); return mMatrixValues[whichValue]; } /** * Resets the Matrix back to FIT_CENTER, and then displays it.s */ private void resetMatrix() { mSuppMatrix.reset(); setImageViewMatrix(getDrawMatrix()); checkMatrixBounds(); } private void setImageViewMatrix(Matrix matrix) { ImageView imageView = getImageView(); if (null != imageView) { checkImageViewScaleType(); imageView.setImageMatrix(matrix); // Call MatrixChangedListener if needed if (null != mMatrixChangeListener) { // 回调 RectF displayRect = getDisplayRect(matrix); if (null != displayRect) { mMatrixChangeListener.onMatrixChanged(displayRect); } } } } /** * Calculate Matrix for FIT_CENTER * * @param d - Drawable being displayed */ private void updateBaseMatrix(Drawable d) { // for BaseMatrix 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) { // CENTER_CROP需要填满Imageview,所以取大 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) { // CENTER_INSIDE需要内容完整显示,所以取小,并且CENTER_INSIDE不能放大哦,即不可大于1 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); 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(); } private int getImageViewWidth(ImageView imageView) { if (null == imageView) return 0; return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); } private int getImageViewHeight(ImageView imageView) { if (null == imageView) return 0; return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); } private class AnimatedZoomRunnable implements Runnable { private final float mFocalX, mFocalY; private final long mStartTime; private final float mZoomStart, mZoomEnd; public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { mFocalX = focalX; // 缩放中心x mFocalY = focalY; // 缩放中心y mStartTime = System.currentTimeMillis(); mZoomStart = currentZoom; mZoomEnd = targetZoom; } @Override public void run() { ImageView imageView = getImageView(); if (imageView == null) { return; } float t = interpolate(); float scale = mZoomStart + t * (mZoomEnd - mZoomStart); float deltaScale = scale / getScale(); onScale(deltaScale, mFocalX, mFocalY); // We haven't hit our target scale yet, so post ourselves again if (t < 1f) { Compat.postOnAnimation(imageView, this); // 隔断时间再次执行run } } private float interpolate() { // 获得比例时间t: 0---1 float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION; t = Math.min(1f, t); // 确保不大于1 t = sInterpolator.getInterpolation(t);//根据差值器计算出输出值,有个变化效果,什么时候快慢 return t; } } private class FlingRunnable implements Runnable { private final ScrollerProxy mScroller; private int mCurrentX, mCurrentY; public FlingRunnable(Context context) { mScroller = ScrollerProxy.getScroller(context); } public void cancelFling() { mScroller.forceFinished(true); } public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) { // 假设手向左滑动了,那么内容区域应该也向左Fling,scroll的速度应该大于0 // 这样内容才会向左移动 final RectF rect = getDisplayRect(); // 获得当前Imageview中内容的显示区域 if (null == rect) { return; } // 这个值不用纠结,需要结合maxX才有意义 final int startX = Math.round(-rect.left); final int minX, maxX, minY, maxY; if (viewWidth < rect.width()) { minX = 0; maxX = Math.round(rect.width() - viewWidth); // 移动的最大范围:maxX - startX = rect.width() - viewWidth + rect.left // 因为内容区域大,所以rect.left <= 0的。自己画图更容易理解。 // 因此,只要结果是rect.width() - viewWidth + rect.left,那么maxX和startX是多少没关系 } else { minX = maxX = startX; } final int startY = Math.round(-rect.top); if (viewHeight < rect.height()) { minY = 0; maxY = Math.round(rect.height() - viewHeight); } else { minY = maxY = startY; } mCurrentX = startX; mCurrentY = startY; // If we actually can move, fling the scroller if (startX != maxX || startY != maxY) { mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); // 注意,不是每次结果都会移动到最大的值的,这和速度有关系了 // velocityX足够大,那么移动的变化量是可以达到rect.width() - viewWidth + rect.left // 否则变化量只会小于rect.width() - viewWidth + rect.left了 } } @Override public void run() { if (mScroller.isFinished()) { return; // remaining post that should not be handled } ImageView imageView = getImageView(); if (null != imageView && mScroller.computeScrollOffset()) { final int newX = mScroller.getCurrX(); final int newY = mScroller.getCurrY(); // 假设手向左滑动了,那么内容区域应该也向左Fling,此时scroll的值是变大的。 // 可惜scroll只是变化过程的计算者,执行者是matrix。 // 而要让内容左移,那么matrix的值需要小于0才行,因此mCurrentX - newX // 如果Fling传入的速度和现在思路相反,那就可以newX -mCurrentX。 mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); setImageViewMatrix(getDrawMatrix()); mCurrentX = newX; mCurrentY = newY; // Post On animation 循环执行这个run Compat.postOnAnimation(imageView, this); } } } }
2 DefaultOnDoubleTapListener
上面设置的多击事件public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener { @Override public boolean onSingleTapConfirmed(MotionEvent e) { if (this.photoViewAttacher == null) return false; ImageView imageView = photoViewAttacher.getImageView(); if (null != photoViewAttacher.getOnPhotoTapListener()) { final RectF displayRect = photoViewAttacher.getDisplayRect(); if (null != displayRect) { final float x = e.getX(), y = e.getY(); // Check to see if the user tapped on the photo if (displayRect.contains(x, y)) { float xResult = (x - displayRect.left) / displayRect.width(); // smart float yResult = (y - displayRect.top) / displayRect.height(); photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult); return true; } } } if (null != photoViewAttacher.getOnViewTapListener()) { photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY()); } return false; } @Override public boolean onDoubleTap(MotionEvent ev) { if (photoViewAttacher == null) return false; try { float scale = photoViewAttacher.getScale(); float x = ev.getX(); float y = ev.getY(); 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; } @Override public boolean onDoubleTapEvent(MotionEvent e) { // Wait for the confirmed onDoubleTap() instead return false; } }
小结
通过上面源码,可以找到Photoview的各种事件处理流程,比如单机,双击,拖动,Fling,缩放,以及对父view的事件处理机制等等,看懂了上面代码,相信以后的触摸事件处理会更上手了。关于scale_type:可以看出,Imageview总是设置为Matrix的,在xml中配置为其他了,这里也会修改;
但是代码中却支持其他类型,原因就是Imageview依然是Matrix的,但PhotoViewAttacher中内部会维护一个用户希望的scaleType类型,在进行base矩阵初始化时updateBaseMatrix,会根据内部的类型去动态改变显示的位置等。并且在checkMatrixBounds中,也会动态去改变调整其范围。
总之,用matrix去实现了用户希望的scaletype,但是scaleType需要设定在PhotoViewAttacher中才有效果,
即用户调用setScaleType(ScaleType scaleType)才行,否则内部默认是FitCenter,而Imageview一直为matrix。
不过注意setScaleType(ScaleType scaleType)不要传入matrix,否则会异常。
相关文章推荐
- 8051汇编命令记录。
- hdu 1551 Cable master(二分法)
- 市面上需要关注的物联网操作系统
- Device eth0 does not seem to be present,delaying initialization.
- expect
- 域传送漏洞总结
- 机器学习实战Logistic回归笔记
- 我看原型链
- Leetcode229 Majority Element II
- C++标准库笔记之一 Chapter 3 New Language Features
- HDU 5035 Delivery (2014年北京赛区网络赛D题)
- Android之Thread与Service
- mongodb学习04 操作详解(2)
- KVC的步骤 以及重点
- xml的增,删,改,查
- C++Primer新笔记之----第12章 动态内存与智能指针
- 已知二叉树的前序和中序遍历,构建该二叉树
- parseInt() 和强制转换的区别(int)
- VirtualBox快捷键
- 第三方应用分享到微信朋友圈功能