diff --git a/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java b/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java index 9a1169ba90..c851e59e20 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java +++ b/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java @@ -46,6 +46,8 @@ import android.support.annotation.NonNull; import android.text.TextUtils; import android.text.format.Formatter; +import android.view.MotionEvent; +import android.view.View; import android.view.ViewGroup; import java.io.Closeable; @@ -1237,4 +1239,22 @@ public static String createFileUriDiskCacheKey(String uri, String filePath) { return uri; } } + + @SuppressWarnings("WeakerAccess") + public static void postOnAnimation(View view, Runnable runnable) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.postOnAnimation(runnable); + } else { + view.postDelayed(runnable, 1000 / 60); + } + } + + public static int getPointerIndex(int action) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + } else { + //noinspection deprecation + return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + } + } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ImageZoomFunction.java b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ImageZoomFunction.java index 85481ded73..8ed31183ea 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ImageZoomFunction.java +++ b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ImageZoomFunction.java @@ -71,13 +71,14 @@ public void onAttachedToWindow() { public void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); - imageZoomer.onDraw(canvas); - if (SketchUtils.sdkSupportBitmapRegionDecoder()) { if (hugeImageViewer.isReady()) { hugeImageViewer.draw(canvas); } } + + // imageZoomer.onDraw 必须在 hugeImageViewer.draw 之后执行,这样才不会被覆盖掉 + imageZoomer.onDraw(canvas); } @Override diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/CompatUtils.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/CompatUtils.java deleted file mode 100755 index b85392c322..0000000000 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/CompatUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright 2011, 2012 Chris Banes. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ - -package me.xiaopan.sketch.zoom; - -import android.annotation.TargetApi; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.view.MotionEvent; -import android.view.View; - -public class CompatUtils { - - private static final int SIXTY_FPS_INTERVAL = 1000 / 60; - - @SuppressWarnings("WeakerAccess") - public static void postOnAnimation(View view, Runnable runnable) { - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { - postOnAnimationJellyBean(view, runnable); - } else { - view.postDelayed(runnable, SIXTY_FPS_INTERVAL); - } - } - - @TargetApi(16) - private static void postOnAnimationJellyBean(View view, Runnable runnable) { - view.postOnAnimation(runnable); - } - - public static int getPointerIndex(int action) { - if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) - return getPointerIndexHoneyComb(action); - else - return getPointerIndexEclair(action); - } - - @SuppressWarnings("deprecation") - @TargetApi(VERSION_CODES.ECLAIR) - private static int getPointerIndexEclair(int action) { - return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; - } - - @TargetApi(VERSION_CODES.HONEYCOMB) - private static int getPointerIndexHoneyComb(int action) { - return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; - } - -} diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/FlingTranslateRunner.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/FlingRunner.java similarity index 70% rename from sketch/src/main/java/me/xiaopan/sketch/zoom/FlingTranslateRunner.java rename to sketch/src/main/java/me/xiaopan/sketch/zoom/FlingRunner.java index 5f6cb21d74..45e659da48 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/FlingTranslateRunner.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/FlingRunner.java @@ -16,36 +16,40 @@ package me.xiaopan.sketch.zoom; -import android.content.Context; import android.graphics.RectF; import android.widget.ImageView; import me.xiaopan.sketch.SLog; +import me.xiaopan.sketch.util.SketchUtils; import me.xiaopan.sketch.zoom.scrollerproxy.ScrollerProxy; -class FlingTranslateRunner implements Runnable { - private final ScrollerProxy mScroller; - private ZoomManager zoomManager; - private int mCurrentX, mCurrentY; +class FlingRunner implements Runnable { + private ImageZoomer imageZoomer; + private ScaleDragHelper scaleDragHelper; - FlingTranslateRunner(Context context, ZoomManager zoomManager) { - this.mScroller = ScrollerProxy.getScroller(context); - this.zoomManager = zoomManager; + private ScrollerProxy scroller; + private int currentX; + private int currentY; + + FlingRunner(ImageZoomer imageZoomer, ScaleDragHelper scaleDragHelper) { + this.scroller = ScrollerProxy.getScroller(imageZoomer.getImageView().getContext()); + this.imageZoomer = imageZoomer; + this.scaleDragHelper = scaleDragHelper; } void fling(int velocityX, int velocityY) { - if (!zoomManager.getImageZoomer().isWorking()) { + if (!imageZoomer.isWorking()) { SLog.w(ImageZoomer.NAME, "not working. fling"); return; } RectF drawRectF = new RectF(); - zoomManager.getDrawRect(drawRectF); + scaleDragHelper.getDrawRect(drawRectF); if (drawRectF.isEmpty()) { return; } - Size viewSize = zoomManager.getImageZoomer().getViewSize(); + Size viewSize = imageZoomer.getViewSize(); final int imageViewWidth = viewSize.getWidth(); final int imageViewHeight = viewSize.getHeight(); @@ -73,13 +77,13 @@ void fling(int velocityX, int velocityY) { // If we actually can move, fling the scroller if (startX != maxX || startY != maxY) { - mCurrentX = startX; - mCurrentY = startY; - mScroller.fling(startX, startY, velocityX, velocityY, minX, + currentX = startX; + currentY = startY; + scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); } - ImageView imageView = zoomManager.getImageZoomer().getImageView(); + ImageView imageView = imageZoomer.getImageView(); imageView.removeCallbacks(this); imageView.post(this); } @@ -87,33 +91,33 @@ void fling(int velocityX, int velocityY) { @Override public void run() { // remaining post that should not be handled - if (mScroller.isFinished()) { + if (scroller.isFinished()) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(ImageZoomer.NAME, "finished. fling run"); } return; } - if (!zoomManager.getImageZoomer().isWorking()) { + if (!imageZoomer.isWorking()) { SLog.w(ImageZoomer.NAME, "not working. fling run"); return; } - if (!mScroller.computeScrollOffset()) { + if (!scroller.computeScrollOffset()) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(ImageZoomer.NAME, "scroll finished. fling run"); } return; } - final int newX = mScroller.getCurrX(); - final int newY = mScroller.getCurrY(); - zoomManager.translateBy(mCurrentX - newX, mCurrentY - newY); - mCurrentX = newX; - mCurrentY = newY; + final int newX = scroller.getCurrX(); + final int newY = scroller.getCurrY(); + scaleDragHelper.translateBy(currentX - newX, currentY - newY); + currentX = newX; + currentY = newY; // Post On animation - CompatUtils.postOnAnimation(zoomManager.getImageZoomer().getImageView(), this); + SketchUtils.postOnAnimation(imageZoomer.getImageView(), this); } @SuppressWarnings("WeakerAccess") @@ -122,10 +126,10 @@ public void cancelFling() { SLog.d(ImageZoomer.NAME, "cancel fling"); } - if (mScroller != null) { - mScroller.forceFinished(true); + if (scroller != null) { + scroller.forceFinished(true); } - ImageView imageView = zoomManager.getImageZoomer().getImageView(); + ImageView imageView = imageZoomer.getImageView(); if (imageView != null) { imageView.removeCallbacks(this); } diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/ImageZoomer.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/ImageZoomer.java index 3bb30d359e..3661a4f022 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/ImageZoomer.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/ImageZoomer.java @@ -21,7 +21,6 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.drawable.Drawable; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; @@ -32,47 +31,51 @@ import java.util.ArrayList; import me.xiaopan.sketch.SLog; -import me.xiaopan.sketch.drawable.SketchDrawable; -import me.xiaopan.sketch.drawable.SketchLoadingDrawable; -import me.xiaopan.sketch.util.SketchUtils; -// TODO 解决嵌套在别的可滑动View中时,会导致ArrayIndexOutOfBoundsException异常,初步猜测requestDisallowInterceptTouchEvent引起的 -@SuppressWarnings("SuspiciousNameCombination") +/** + * 图片缩放器,接收触摸事件,变换 Matrix,改变图片的显示效果,代理点击和长按事件 + */ +// TODO 解决嵌套在别的可滑动 View 中时,会导致 ArrayIndexOutOfBoundsException 异常,初步猜测 requestDisallowInterceptTouchEvent 引起的 public class ImageZoomer { public static final String NAME = "ImageZoomer"; private ImageView imageView; - private ScaleType scaleType; + private ScaleType scaleType; // ImageView 原本的 ScaleType - private Size viewSize = new Size(); - private Size imageSize = new Size(); - private Size drawableSize = new Size(); + private Sizes sizes = new Sizes(); + private Scales scales = new Scales(); // 根据预览图尺寸、原始图尺寸和 ImageView 尺寸计算出的缩放比例 - // config private int rotateDegrees; // 旋转角度 private int zoomDuration = 200; // 双击缩放动画持续时间 private boolean readMode; // 阅读模式下,竖图将默认横向充满屏幕 private Interpolator zoomInterpolator = new AccelerateDecelerateInterpolator(); private boolean allowParentInterceptOnEdge = true; // 允许父 ViewGroup 在滑动到边缘时拦截事件 - private OnViewTapListener onViewTapListener; private OnDragFlingListener onDragFlingListener; private OnScaleChangeListener onScaleChangeListener; private OnRotateChangeListener onRotateChangeListener; + private OnViewTapListener onViewTapListener; private OnViewLongPressListener onViewLongPressListener; private ArrayList onMatrixChangeListenerList; - private ZoomManager zoomManager; + private TapHelper tapHelper; + private ScaleDragHelper scaleDragHelper; + private ScrollBarHelper scrollBarHelper; // 挪到 外面去 public ImageZoomer(ImageView imageView) { Context appContext = imageView.getContext().getApplicationContext(); this.imageView = imageView; this.scaleType = imageView.getScaleType(); - this.zoomManager = new ZoomManager(appContext, this); + + this.tapHelper = new TapHelper(appContext, this); + this.scaleDragHelper = new ScaleDragHelper(appContext, this); + this.scrollBarHelper = new ScrollBarHelper(appContext, this); } + /* -----------ImageView 回调----------- */ + /** * 绘制回调 */ @@ -81,14 +84,20 @@ public void onDraw(Canvas canvas) { return; } - zoomManager.onDraw(canvas); + scrollBarHelper.onDraw(canvas); } /** * 事件回调 */ public boolean onTouchEvent(MotionEvent event) { - return isWorking() && zoomManager.onTouchEvent(event); + if (!isWorking()) { + return false; + } + + boolean scaleAndDragConsumed = scaleDragHelper.onTouchEvent(event); + boolean tapConsumed = tapHelper.onTouchEvent(event); + return scaleAndDragConsumed || tapConsumed; } /** @@ -101,8 +110,27 @@ public void onSizeChanged() { reset(); } + + /* -----------内部组件回调----------- */ + + + void onMatrixChanged() { + // 在 setImageMatrix 前面执行,省了再执行一次 imageView.invalidate() + scrollBarHelper.onMatrixChanged(); + + imageView.setImageMatrix(scaleDragHelper.getDrawMatrix()); + + if (onMatrixChangeListenerList != null && !onMatrixChangeListenerList.isEmpty()) { + for (int w = 0, size = onMatrixChangeListenerList.size(); w < size; w++) { + onMatrixChangeListenerList.get(w).onMatrixChanged(this); + } + } + } + + /* -----------主要方法----------- */ + /** * 当 ImageView 的 drawable、scaleType、尺寸发生改变或旋转角度、阅读模式修改了需要调用此方法重置 * @@ -110,64 +138,156 @@ public void onSizeChanged() { */ public boolean reset() { recycle(); - resetSizes(); + sizes.resetSizes(imageView); if (!isWorking()) { return false; } imageView.setScaleType(ScaleType.MATRIX); - zoomManager.reset(); + scales.reset(imageView.getContext(), sizes, scaleType, rotateDegrees, readMode); + scaleDragHelper.reset(); return true; } - private void resetSizes() { - final int imageViewWidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); - final int imageViewHeight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); - if (imageViewWidth == 0 || imageViewHeight == 0) { - return; + /** + * 不需要缩放时回收 + */ + public void recycle() { + sizes.clean(); + scales.clean(); + scaleDragHelper.recycle(); + + imageView.setImageMatrix(null); // 恢复 Matrix 这很重要 + imageView.setScaleType(scaleType); // 恢复 ScaleType 这很重要 + } + + + /* -----------交互功能----------- */ + + + /** + * 定位到预览图上指定的位置(不用考虑旋转角度) + */ + @SuppressWarnings("unused") + public boolean location(float x, float y, boolean animate) { + if (!isWorking()) { + SLog.w(NAME, "not working. location"); + return false; } - Drawable drawable = SketchUtils.getLastDrawable(imageView.getDrawable()); - if (drawable == null) { - return; + scaleDragHelper.location(x, y, animate); + return true; + } + + /** + * 定位到预览图上指定的位置(不用考虑旋转角度) + */ + @SuppressWarnings("unused") + public boolean location(float x, float y) { + return location(x, y, false); + } + + /** + * 缩放 + */ + public boolean zoom(float scale, float focalX, float focalY, boolean animate) { + if (!isWorking()) { + SLog.w(NAME, "not working. zoom(float, float, float, boolean)"); + return false; } - final int drawableWidth = drawable.getIntrinsicWidth(); - final int drawableHeight = drawable.getIntrinsicHeight(); - if (drawableWidth == 0 || drawableHeight == 0) { - return; + if (scale < scales.minZoomScale || scale > scales.maxZoomScale) { + SLog.w(NAME, "Scale must be within the range of %s(minScale) and %s(maxScale). %s", + scales.minZoomScale, scales.maxZoomScale, scale); + return false; } - viewSize.set(imageViewWidth, imageViewHeight); - drawableSize.set(drawableWidth, drawableHeight); - if (drawable instanceof SketchDrawable && !(drawable instanceof SketchLoadingDrawable)) { - SketchDrawable sketchDrawable = (SketchDrawable) drawable; - imageSize.set(sketchDrawable.getOriginWidth(), sketchDrawable.getOriginHeight()); - } else { - imageSize.set(drawableWidth, drawableHeight); + scaleDragHelper.zoom(scale, focalX, focalY, animate); + return true; + } + + /** + * 缩放 + */ + public boolean zoom(float scale, boolean animate) { + if (!isWorking()) { + SLog.w(NAME, "not working. zoom(float, boolean)"); + return false; } + + ImageView imageView = getImageView(); + return zoom(scale, imageView.getRight() / 2, imageView.getBottom() / 2, animate); } /** - * 不需要缩放时回收 + * 缩放 */ - public void recycle() { - zoomManager.recycle(); - viewSize.set(0, 0); - imageSize.set(0, 0); - drawableSize.set(0, 0); - imageView.setImageMatrix(null); // 恢复 Matrix 这很重要 - imageView.setScaleType(scaleType); // 恢复 ScaleType 这很重要 + @SuppressWarnings("unused") + public boolean zoom(float scale) { + return zoom(scale, false); + } + + /** + * 获取旋转角度 + */ + public int getRotateDegrees() { + return rotateDegrees; } + + /** + * 旋转图片(会清除已经有的缩放和移动数据,旋转角度会一直存在) + */ + // TODO: 16/9/28 支持旋转动画 + // TODO: 16/9/28 增加手势旋转功能 + // TODO: 16/10/19 研究任意角度旋转和旋转时不清空位移以及缩放信息 + public boolean rotateTo(int degrees) { + if (!isWorking()) { + SLog.w(NAME, "not working. rotateTo"); + return false; + } + + if (this.rotateDegrees == degrees) { + return false; + } + + if (degrees % 90 != 0) { + SLog.w(NAME, "rotate degrees must be in multiples of 90"); + return false; + } + + degrees %= 360; + if (degrees <= 0) { + degrees = 360 - degrees; + } + + this.rotateDegrees = degrees; + reset(); + + if (onRotateChangeListener != null) { + onRotateChangeListener.onRotateChanged(this); + } + + return true; + } + + /** + * 在当前旋转角度的基础上旋转一定角度 + */ + public boolean rotateBy(int degrees) { + return rotateTo(degrees + getRotateDegrees()); + } + + /* -----------可获取信息----------- */ + /** * {@link ImageZoomer} 工作中 */ public boolean isWorking() { - return !drawableSize.isEmpty() && !viewSize.isEmpty() && !imageSize.isEmpty(); + return !sizes.isEmpty(); } /** @@ -181,49 +301,49 @@ public ImageView getImageView() { * 获取 ImageView 的尺寸 */ public Size getViewSize() { - return viewSize; + return sizes.viewSize; } /** * 获取图片原始尺寸 */ public Size getImageSize() { - return imageSize; + return sizes.imageSize; } /** * 获取预览图的尺寸 */ public Size getDrawableSize() { - return drawableSize; + return sizes.drawableSize; } /** * 拷贝绘制 Matrix 的参数 */ public void getDrawMatrix(Matrix matrix) { - matrix.set(zoomManager.getDrawMatrix()); + matrix.set(scaleDragHelper.getDrawMatrix()); } /** * 获取绘制区域 */ public void getDrawRect(RectF rectF) { - zoomManager.getDrawRect(rectF); + scaleDragHelper.getDrawRect(rectF); } /** * 获取预览图上用户可以看到的区域(不受旋转影响) */ public void getVisibleRect(Rect rect) { - zoomManager.getVisibleRect(rect); + scaleDragHelper.getVisibleRect(rect); } /** * 获取当前缩放比例 */ public float getZoomScale() { - return zoomManager.getZoomScale(); + return scaleDragHelper.getZoomScale(); } /** @@ -231,7 +351,7 @@ public float getZoomScale() { */ @SuppressWarnings("unused") public float getBaseZoomScale() { - return zoomManager.getDefaultZoomScale(); + return scaleDragHelper.getDefaultZoomScale(); } /** @@ -239,7 +359,7 @@ public float getBaseZoomScale() { */ @SuppressWarnings("unused") public float getSupportZoomScale() { - return zoomManager.getSupportZoomScale(); + return scaleDragHelper.getSupportZoomScale(); } /** @@ -247,7 +367,7 @@ public float getSupportZoomScale() { */ @SuppressWarnings("unused") public float getFullZoomScale() { - return zoomManager.getFullZoomScale(); + return scales.fullZoomScale; } /** @@ -255,7 +375,7 @@ public float getFullZoomScale() { */ @SuppressWarnings("unused") public float getFillZoomScale() { - return zoomManager.getFillZoomScale(); + return scales.fillZoomScale; } /** @@ -263,7 +383,7 @@ public float getFillZoomScale() { */ @SuppressWarnings("unused") public float getOriginZoomScale() { - return zoomManager.getOriginZoomScale(); + return scales.originZoomScale; } /** @@ -271,7 +391,7 @@ public float getOriginZoomScale() { */ @SuppressWarnings("unused") public float getMinZoomScale() { - return zoomManager.getMinZoomScale(); + return scales.minZoomScale; } /** @@ -279,7 +399,7 @@ public float getMinZoomScale() { */ @SuppressWarnings("unused") public float getMaxZoomScale() { - return zoomManager.getMaxZoomScale(); + return scales.maxZoomScale; } /** @@ -287,135 +407,20 @@ public float getMaxZoomScale() { */ @SuppressWarnings("WeakerAccess") public float[] getDoubleClickZoomScales() { - return zoomManager.getDoubleClickZoomScales(); + return scales.doubleClickZoomScales; } /** * 正在缩放 */ public boolean isZooming() { - return zoomManager.isZooming(); - } - - - /* -----------交互功能----------- */ - - /** - * 定位到预览图上指定的位置(不用考虑旋转角度) - */ - @SuppressWarnings("unused") - public boolean location(float x, float y, boolean animate) { - if (!isWorking()) { - SLog.w(NAME, "not working. location"); - return false; - } - - zoomManager.location(x, y, animate); - return true; - } - - /** - * 定位到预览图上指定的位置(不用考虑旋转角度) - */ - @SuppressWarnings("unused") - public boolean location(float x, float y) { - return location(x, y, false); - } - - /** - * 缩放 - */ - public boolean zoom(float scale, float focalX, float focalY, boolean animate) { - if (!isWorking()) { - SLog.w(NAME, "not working. zoom(float, float, float, boolean)"); - return false; - } - - if (scale < zoomManager.getMinZoomScale() || scale > zoomManager.getMaxZoomScale()) { - SLog.w(NAME, "Scale must be within the range of %s(minScale) and %s(maxScale). %s", - zoomManager.getMinZoomScale(), zoomManager.getMaxZoomScale(), scale); - return false; - } - - zoomManager.zoom(scale, focalX, focalY, animate); - return true; - } - - /** - * 缩放 - */ - public boolean zoom(float scale, boolean animate) { - if (!isWorking()) { - SLog.w(NAME, "not working. zoom(float, boolean)"); - return false; - } - - ImageView imageView = getImageView(); - return zoom(scale, imageView.getRight() / 2, imageView.getBottom() / 2, animate); - } - - /** - * 缩放 - */ - @SuppressWarnings("unused") - public boolean zoom(float scale) { - return zoom(scale, false); - } - - /** - * 获取旋转角度 - */ - public int getRotateDegrees() { - return rotateDegrees; - } - - - /** - * 旋转图片(会清除已经有的缩放和移动数据,旋转角度会一直存在) - */ - // TODO: 16/9/28 支持旋转动画 - // TODO: 16/9/28 增加手势旋转功能 - // TODO: 16/10/19 研究任意角度旋转和旋转时不清空位移以及缩放信息 - public boolean rotateTo(int degrees) { - if (!isWorking()) { - SLog.w(NAME, "not working. rotateTo"); - return false; - } - - if (this.rotateDegrees == degrees) { - return false; - } - - if (degrees % 90 != 0) { - SLog.w(NAME, "rotate degrees must be in multiples of 90"); - return false; - } - - degrees %= 360; - if (degrees <= 0) { - degrees = 360 - degrees; - } - - this.rotateDegrees = degrees; - reset(); - - if (onRotateChangeListener != null) { - onRotateChangeListener.onRotateChanged(this); - } - - return true; - } - - /** - * 在当前旋转角度的基础上旋转一定角度 - */ - public boolean rotateBy(int degrees) { - return rotateTo(degrees + getRotateDegrees()); + return scaleDragHelper.isZooming(); } /* -----------配置----------- */ + /** * 获取缩放动画持续时间 */ @@ -541,10 +546,6 @@ public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener this.onScaleChangeListener = onScaleChangeListener; } - ArrayList getOnMatrixChangeListenerList() { - return onMatrixChangeListenerList; - } - /** * 添加Matrix变化监听器 */ @@ -587,7 +588,6 @@ public void setOnRotateChangeListener(OnRotateChangeListener onRotateChangeListe this.onRotateChangeListener = onRotateChangeListener; } - /** * 飞速拖拽监听器 */ diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/LocationRunner.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/LocationRunner.java index 5ad0cd9cb7..5f597e3bc5 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/LocationRunner.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/LocationRunner.java @@ -16,36 +16,40 @@ package me.xiaopan.sketch.zoom; -import android.content.Context; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageView; import android.widget.Scroller; import me.xiaopan.sketch.SLog; +import me.xiaopan.sketch.util.SketchUtils; /** * 定位执行器 */ class LocationRunner implements Runnable { - private final Scroller mScroller; - private ZoomManager zoomManager; - private int mCurrentX, mCurrentY; + private ImageZoomer imageZoomer; + private ScaleDragHelper scaleDragHelper; - LocationRunner(Context context, ZoomManager zoomManager) { - this.mScroller = new Scroller(context, new AccelerateDecelerateInterpolator()); - this.zoomManager = zoomManager; + private Scroller scroller; + private int currentX; + private int currentY; + + LocationRunner(ImageZoomer imageZoomer, ScaleDragHelper scaleDragHelper) { + this.scroller = new Scroller(imageZoomer.getImageView().getContext(), new AccelerateDecelerateInterpolator()); + this.imageZoomer = imageZoomer; + this.scaleDragHelper = scaleDragHelper; } /** * 定位到预览图上指定的位置 */ void location(int startX, int startY, int endX, int endY) { - mCurrentX = startX; - mCurrentY = startY; + currentX = startX; + currentY = startY; - mScroller.startScroll(startX, startY, endX - startX, endY - startY, 300); + scroller.startScroll(startX, startY, endX - startX, endY - startY, 300); - ImageView imageView = zoomManager.getImageZoomer().getImageView(); + ImageView imageView = imageZoomer.getImageView(); imageView.removeCallbacks(this); imageView.post(this); } @@ -53,45 +57,45 @@ void location(int startX, int startY, int endX, int endY) { @Override public void run() { // remaining post that should not be handled - if (mScroller.isFinished()) { + if (scroller.isFinished()) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(ImageZoomer.NAME, "finished. location run"); } return; } - if (!zoomManager.getImageZoomer().isWorking()) { + if (!imageZoomer.isWorking()) { SLog.w(ImageZoomer.NAME, "not working. location run"); - mScroller.forceFinished(true); + scroller.forceFinished(true); return; } - if (!mScroller.computeScrollOffset()) { + if (!scroller.computeScrollOffset()) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(ImageZoomer.NAME, "scroll finished. location run"); } return; } - final int newX = mScroller.getCurrX(); - final int newY = mScroller.getCurrY(); - final float dx = mCurrentX - newX; - final float dy = mCurrentY - newY; - zoomManager.translateBy(dx, dy); - mCurrentX = newX; - mCurrentY = newY; + final int newX = scroller.getCurrX(); + final int newY = scroller.getCurrY(); + final float dx = currentX - newX; + final float dy = currentY - newY; + scaleDragHelper.translateBy(dx, dy); + currentX = newX; + currentY = newY; // Post On animation - CompatUtils.postOnAnimation(zoomManager.getImageZoomer().getImageView(), this); + SketchUtils.postOnAnimation(imageZoomer.getImageView(), this); } boolean isRunning() { - return !mScroller.isFinished(); + return !scroller.isFinished(); } void cancel() { - mScroller.forceFinished(true); - ImageView imageView = zoomManager.getImageZoomer().getImageView(); + scroller.forceFinished(true); + ImageView imageView = imageZoomer.getImageView(); if (imageView != null) { imageView.removeCallbacks(this); } diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomManager.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/ScaleDragHelper.java similarity index 54% rename from sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomManager.java rename to sketch/src/main/java/me/xiaopan/sketch/zoom/ScaleDragHelper.java index 2b04557432..fcdd52b3bc 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomManager.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/ScaleDragHelper.java @@ -17,19 +17,15 @@ package me.xiaopan.sketch.zoom; import android.content.Context; -import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ViewParent; import android.widget.ImageView; import android.widget.ImageView.ScaleType; -import java.util.List; - import me.xiaopan.sketch.SLog; import me.xiaopan.sketch.Sketch; import me.xiaopan.sketch.decode.ImageSizeCalculator; @@ -39,66 +35,73 @@ import me.xiaopan.sketch.zoom.gestures.ScaleDragGestureDetector; import me.xiaopan.sketch.zoom.gestures.ScaleDragGestureDetectorCompat; -public class ZoomManager { - private static final String NAME = "ImageZoomer"; +import static me.xiaopan.sketch.zoom.ImageZoomer.NAME; + +/** + * 缩放和拖拽处理,控制 Matrix 变化,更新 Matrix + */ +class ScaleDragHelper implements OnScaleDragGestureListener, ActionListener { private static final int EDGE_NONE = -1; private static final int EDGE_START = 0; private static final int EDGE_END = 1; private static final int EDGE_BOTH = 2; - private Context context; private ImageZoomer imageZoomer; - private Matrix defaultMatrix = new Matrix(); // 存储默认的缩放和位移信息 + private Matrix baseMatrix = new Matrix(); // 存储默认的缩放和位移信息 private Matrix supportMatrix = new Matrix(); // 存储用户通过触摸事件产生的缩放、位移和外部设置的旋转信息 private Matrix drawMatrix = new Matrix(); // 存储 configMatrix 和 userMatrix 融合后的信息,用于绘制 - private RectF tempDisplayRectF = new RectF(); - private int horScrollEdge = EDGE_NONE; // 横向滚动边界 - private int verScrollEdge = EDGE_NONE; // 竖向滚动边界 - private boolean zooming; // 缩放中状态 - private ZoomScales zoomScales = new ZoomScales(); - private GestureDetector tapGestureDetector; // 点击手势识别器 + private FlingRunner flingRunner; // 执行飞速滚动 private LocationRunner locationRunner; // 定位执行器 - private FlingTranslateRunner flingTranslateRunner; // 执行飞速滚动 private ScaleDragGestureDetector scaleDragGestureDetector; // 缩放和拖拽手势识别器 + + private RectF tempDisplayRectF = new RectF(); + private int horScrollEdge = EDGE_NONE; // 横向滚动边界 + private int verScrollEdge = EDGE_NONE; // 竖向滚动边界 private boolean disallowParentInterceptTouchEvent; // 控制滑动或缩放中到达边缘了依然禁止父类拦截事件 private float tempLastScaleFocusX, tempLastScaleFocusY; // 缓存最后一次缩放手势的坐标,在恢复缩放比例时使用 - private ScrollBar scrollBar; - private OnScaleDragGestureListenerImpl scaleDragGestureListener = new OnScaleDragGestureListenerImpl(); + private boolean zooming; // 缩放中状态 - public ZoomManager(Context context, ImageZoomer imageZoomer) { + public ScaleDragHelper(Context context, ImageZoomer imageZoomer) { + Context appContext = context.getApplicationContext(); this.imageZoomer = imageZoomer; - this.context = context.getApplicationContext(); - - this.tapGestureDetector = new GestureDetector(this.context, new TapListener(imageZoomer)); - this.scaleDragGestureDetector = ScaleDragGestureDetectorCompat.newInstance(this.context, scaleDragGestureListener); - this.scaleDragGestureDetector.setActionListener(new ActionListenerImpl()); - this.scrollBar = new ScrollBar(this.context, imageZoomer); + this.scaleDragGestureDetector = ScaleDragGestureDetectorCompat.newInstance(appContext, this); + this.scaleDragGestureDetector.setActionListener(this); } - - /* -----------回调方法----------- */ - /** - * 绘制回调 + * 获取边界名称,log 专用 */ - public void onDraw(Canvas canvas) { - scrollBar.drawScrollBar(canvas); + private static String getScrollEdgeName(int scrollEdge) { + if (scrollEdge == EDGE_NONE) { + return "NONE"; + } else if (scrollEdge == EDGE_START) { + return "START"; + } else if (scrollEdge == EDGE_END) { + return "END"; + } else if (scrollEdge == EDGE_BOTH) { + return "BOTH"; + } else { + return "UNKNOWN"; + } } - /** - * 触摸事件回调 - */ - public boolean onTouchEvent(MotionEvent event) { - // 定位操作不能被打断 + private static void requestDisallowInterceptTouchEvent(ImageView imageView, boolean disallowIntercept) { + ViewParent parent = imageView.getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(disallowIntercept); + } + } + + boolean onTouchEvent(MotionEvent event) {// 定位操作不能被打断 if (locationRunner != null) { if (locationRunner.isRunning()) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(NAME, "disallow parent intercept touch event. location running"); } - requestDisallowInterceptTouchEvent(true); + requestDisallowInterceptTouchEvent(imageZoomer.getImageView(), true); return true; } locationRunner = null; @@ -115,27 +118,175 @@ public boolean onTouchEvent(MotionEvent event) { disallowParentInterceptTouchEvent = !beforeInScaling && !afterInScaling && beforeInDragging && afterInDragging; - // 点击手势处理 - boolean tapHandled = tapGestureDetector != null && tapGestureDetector.onTouchEvent(event); + return scaleDragHandled; + } + + @Override + public void onActionDown(MotionEvent ev) { + tempLastScaleFocusX = 0; + tempLastScaleFocusY = 0; - return scaleDragHandled || tapHandled; + // 上来就禁止父View拦截事件 + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "disallow parent intercept touch event. action down"); + } + requestDisallowInterceptTouchEvent(imageZoomer.getImageView(), true); + + // 取消快速滚动 + cancelFling(); } + @Override + public void onDrag(float dx, float dy) { + ImageView imageView = imageZoomer.getImageView(); + if (imageView == null) { + return; + } - /* -----------主要方法----------- */ + // 缩放中不能拖拽 + if (scaleDragGestureDetector.isScaling()) { + return; + } - public void reset() { - zoomScales.reset(context, imageZoomer.getViewSize(), imageZoomer.getImageSize(), imageZoomer.getDrawableSize(), - imageZoomer.getScaleType(), imageZoomer.getRotateDegrees(), imageZoomer.isReadMode()); + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "drag. dx: %s, dy: %s", dx, dy); + } - resetDefaultMatrix(); - resetSupportMatrix(); + supportMatrix.postTranslate(dx, dy); checkAndApplyMatrix(); + + if (!imageZoomer.isAllowParentInterceptOnEdge() || scaleDragGestureDetector.isScaling() || disallowParentInterceptTouchEvent) { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "disallow parent intercept touch event. onDrag. allowParentInterceptOnEdge=%s, scaling=%s, tempDisallowParentInterceptTouchEvent=%s", + imageZoomer.isAllowParentInterceptOnEdge(), scaleDragGestureDetector.isScaling(), disallowParentInterceptTouchEvent); + } + requestDisallowInterceptTouchEvent(imageZoomer.getImageView(), true); + return; + } + + // 滑动到边缘时父类可以拦截触摸事件,但暂时不处理顶部和底部边界 +// if (horScrollEdge == EDGE_BOTH || (horScrollEdge == EDGE_START && dx >= 1f) || (horScrollEdge == EDGE_END && dx <= -1f) +// || verScrollEdge == EDGE_BOTH || (verScrollEdge == EDGE_START && dy >= 1f) || (verScrollEdge == EDGE_END && dy <= -1f)) { + if (horScrollEdge == EDGE_BOTH || (horScrollEdge == EDGE_START && dx >= 1f) || (horScrollEdge == EDGE_END && dx <= -1f)) { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "allow parent intercept touch event. onDrag. scrollEdge=%s-%s", + getScrollEdgeName(horScrollEdge), getScrollEdgeName(verScrollEdge)); + } + requestDisallowInterceptTouchEvent(imageZoomer.getImageView(), false); + } else { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "disallow parent intercept touch event. onDrag. scrollEdge=%s-%s", + getScrollEdgeName(horScrollEdge), getScrollEdgeName(verScrollEdge)); + } + requestDisallowInterceptTouchEvent(imageZoomer.getImageView(), true); + } + } + + @Override + public void onFling(float startX, float startY, float velocityX, float velocityY) { + flingRunner = new FlingRunner(imageZoomer, ScaleDragHelper.this); + flingRunner.fling((int) velocityX, (int) velocityY); + + ImageZoomer.OnDragFlingListener onDragFlingListener = imageZoomer.getOnDragFlingListener(); + if (onDragFlingListener != null) { + onDragFlingListener.onFling(startX, startY, velocityX, velocityY); + } + } + + @Override + public void onScale(float scaleFactor, float focusX, float focusY) { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "scale. scaleFactor: %s, dx: %s, dy: %s", scaleFactor, focusX, focusY); + } + + tempLastScaleFocusX = focusX; + tempLastScaleFocusY = focusY; + + float oldSuppScale = getSupportZoomScale(); + float newSuppScale = oldSuppScale * scaleFactor; + + if (scaleFactor > 1.0f) { + // 放大的时候,如果当前已经超过最大缩放比例,就调慢缩放速度 + // 这样就能模拟出超过最大缩放比例时很难再继续放大有种拉橡皮筋的感觉 + float maxSuppScale = imageZoomer.getMaxZoomScale() / SketchUtils.getMatrixScale(baseMatrix); + if (oldSuppScale >= maxSuppScale) { + float addScale = newSuppScale - oldSuppScale; + addScale *= 0.4; + newSuppScale = oldSuppScale + addScale; + scaleFactor = newSuppScale / oldSuppScale; + } + } else if (scaleFactor < 1.0f) { + // 缩小的时候,如果当前已经小于最小缩放比例,就调慢缩放速度 + // 这样就能模拟出小于最小缩放比例时很难再继续缩小有种拉橡皮筋的感觉 + float minSuppScale = imageZoomer.getMinZoomScale() / SketchUtils.getMatrixScale(baseMatrix); + if (oldSuppScale <= minSuppScale) { + float addScale = newSuppScale - oldSuppScale; + addScale *= 0.4; + newSuppScale = oldSuppScale + addScale; + scaleFactor = newSuppScale / oldSuppScale; + } + } + + supportMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); + checkAndApplyMatrix(); + + ImageZoomer.OnScaleChangeListener onScaleChangeListener = imageZoomer.getOnScaleChangeListener(); + if (onScaleChangeListener != null) { + onScaleChangeListener.onScaleChanged(scaleFactor, focusX, focusY); + } + } + + @Override + public boolean onScaleBegin() { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "scale begin"); + } + + zooming = true; + return true; + } + + @Override + public void onScaleEnd() { + if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { + SLog.d(NAME, "scale end"); + } + + float currentScale = SketchUtils.formatFloat(getZoomScale(), 2); + boolean overMinZoomScale = currentScale < SketchUtils.formatFloat(imageZoomer.getMinZoomScale(), 2); + boolean overMaxZoomScale = currentScale > SketchUtils.formatFloat(imageZoomer.getMaxZoomScale(), 2); + if (!overMinZoomScale && !overMaxZoomScale) { + zooming = false; + imageZoomer.onMatrixChanged(); + } + } + + @Override + public void onActionUp(MotionEvent ev) { + float currentScale = SketchUtils.formatFloat(getZoomScale(), 2); + if (currentScale < SketchUtils.formatFloat(imageZoomer.getMinZoomScale(), 2)) { + // 如果当前缩放倍数小于最小倍数就回滚至最小倍数 + RectF drawRectF = new RectF(); + getDrawRect(drawRectF); + if (!drawRectF.isEmpty()) { + zoom(imageZoomer.getMinZoomScale(), drawRectF.centerX(), drawRectF.centerY(), true); + } + } else if (currentScale > SketchUtils.formatFloat(imageZoomer.getMaxZoomScale(), 2)) { + // 如果当前缩放倍数大于最大倍数就回滚至最大倍数 + if (tempLastScaleFocusX != 0 && tempLastScaleFocusY != 0) { + zoom(imageZoomer.getMaxZoomScale(), tempLastScaleFocusX, tempLastScaleFocusY, true); + } + } + } + + @Override + public void onActionCancel(MotionEvent ev) { + onActionUp(ev); } @SuppressWarnings("ConstantConditions") - private void resetDefaultMatrix() { - defaultMatrix.reset(); + private void resetBaseMatrix() { + baseMatrix.reset(); Size viewSize = imageZoomer.getViewSize(); Size imageSize = imageZoomer.getImageSize(); @@ -153,25 +304,25 @@ private void resetDefaultMatrix() { boolean imageThanViewLarge = drawableWidth > viewSize.getWidth() || drawableHeight > viewSize.getHeight(); if (scaleType == ScaleType.CENTER || (scaleType == ScaleType.CENTER_INSIDE && !imageThanViewLarge)) { - ImageSizeCalculator sizeCalculator = Sketch.with(context).getConfiguration().getSizeCalculator(); + ImageSizeCalculator sizeCalculator = Sketch.with(imageZoomer.getImageView().getContext()).getConfiguration().getSizeCalculator(); if (readMode && sizeCalculator.canUseReadModeByHeight(imageWidth, imageHeight)) { - defaultMatrix.postScale(widthScale, widthScale); + baseMatrix.postScale(widthScale, widthScale); } else if (readMode && sizeCalculator.canUseReadModeByWidth(imageWidth, imageHeight)) { - defaultMatrix.postScale(heightScale, heightScale); + baseMatrix.postScale(heightScale, heightScale); } else { - defaultMatrix.postTranslate((viewSize.getWidth() - drawableWidth) / 2F, (viewSize.getHeight() - drawableHeight) / 2F); + baseMatrix.postTranslate((viewSize.getWidth() - drawableWidth) / 2F, (viewSize.getHeight() - drawableHeight) / 2F); } } else if (scaleType == ScaleType.CENTER_CROP) { float scale = Math.max(widthScale, heightScale); - defaultMatrix.postScale(scale, scale); - defaultMatrix.postTranslate((viewSize.getWidth() - drawableWidth * scale) / 2F, (viewSize.getHeight() - drawableHeight * scale) / 2F); + baseMatrix.postScale(scale, scale); + baseMatrix.postTranslate((viewSize.getWidth() - drawableWidth * scale) / 2F, (viewSize.getHeight() - drawableHeight * scale) / 2F); } else if (scaleType == ScaleType.FIT_START || scaleType == ScaleType.FIT_CENTER || scaleType == ScaleType.FIT_END || (scaleType == ScaleType.CENTER_INSIDE && imageThanViewLarge)) { - ImageSizeCalculator sizeCalculator = Sketch.with(context).getConfiguration().getSizeCalculator(); + ImageSizeCalculator sizeCalculator = Sketch.with(imageZoomer.getImageView().getContext()).getConfiguration().getSizeCalculator(); if (readMode && sizeCalculator.canUseReadModeByHeight(imageWidth, imageHeight)) { - defaultMatrix.postScale(widthScale, widthScale); + baseMatrix.postScale(widthScale, widthScale); } else if (readMode && sizeCalculator.canUseReadModeByWidth(imageWidth, imageHeight)) { - defaultMatrix.postScale(heightScale, heightScale); + baseMatrix.postScale(heightScale, heightScale); } else { RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); RectF mTempDst = new RectF(0, 0, viewSize.getWidth(), viewSize.getHeight()); @@ -183,12 +334,12 @@ private void resetDefaultMatrix() { } else { scaleToFit = Matrix.ScaleToFit.CENTER; } - defaultMatrix.setRectToRect(mTempSrc, mTempDst, scaleToFit); + baseMatrix.setRectToRect(mTempSrc, mTempDst, scaleToFit); } } else if (scaleType == ScaleType.FIT_XY) { RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); RectF mTempDst = new RectF(0, 0, viewSize.getWidth(), viewSize.getHeight()); - defaultMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.FILL); + baseMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.FILL); } } @@ -197,9 +348,6 @@ private void resetSupportMatrix() { supportMatrix.postRotate(imageZoomer.getRotateDegrees()); } - /** - * 检查 Matrix 边界并应用 - */ private void checkAndApplyMatrix() { if (!checkMatrixBounds()) { return; @@ -210,20 +358,9 @@ private void checkAndApplyMatrix() { throw new IllegalStateException("ImageView scaleType must be is MATRIX"); } - scrollBar.matrixChanged(); - imageView.setImageMatrix(getDrawMatrix()); - - List onMatrixChangeListenerList = imageZoomer.getOnMatrixChangeListenerList(); - if (onMatrixChangeListenerList != null && !onMatrixChangeListenerList.isEmpty()) { - for (int w = 0, size = onMatrixChangeListenerList.size(); w < size; w++) { - onMatrixChangeListenerList.get(w).onMatrixChanged(imageZoomer); - } - } + imageZoomer.onMatrixChanged(); } - /** - * 检查应用 Matrix 后的边界,防止超出范围 - */ private boolean checkMatrixBounds() { final RectF drawRectF = tempDisplayRectF; getDrawRect(drawRectF); @@ -300,42 +437,38 @@ private boolean checkMatrixBounds() { return true; } - public void recycle() { - cancelFling(); + void cancelFling() { + if (flingRunner != null) { + flingRunner.cancelFling(); + flingRunner = null; + } } - private void cancelFling() { - if (flingTranslateRunner != null) { - flingTranslateRunner.cancelFling(); - flingTranslateRunner = null; - } + void reset() { + resetBaseMatrix(); + resetSupportMatrix(); + checkAndApplyMatrix(); + } + + void recycle() { + cancelFling(); } - /** - * 移动一段距离 - */ void translateBy(float dx, float dy) { supportMatrix.postTranslate(dx, dy); checkAndApplyMatrix(); } - private void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - ImageView imageView = imageZoomer.getImageView(); - ViewParent parent = imageView.getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(disallowIntercept); - } + void scaleBy(float addScale, float focalX, float focalY) { + supportMatrix.postScale(addScale, addScale, focalX, focalY); + checkAndApplyMatrix(); } - - /* -----------交互方法----------- */ - - /** * 定位到预览图上指定的位置(不用考虑旋转角度) */ @SuppressWarnings("unused") - public void location(float x, float y, boolean animate) { + void location(float x, float y, boolean animate) { Size imageViewSize = imageZoomer.getViewSize(); Size drawableSize = imageZoomer.getDrawableSize(); @@ -356,9 +489,9 @@ public void location(float x, float y, boolean animate) { // 充满的时候是无法移动的,因此先放到最大 final float scale = SketchUtils.formatFloat(getZoomScale(), 2); - final float fullZoomScale = SketchUtils.formatFloat(getFullZoomScale(), 2); + final float fullZoomScale = SketchUtils.formatFloat(imageZoomer.getFullZoomScale(), 2); if (scale == fullZoomScale) { - float[] zoomScales = getDoubleClickZoomScales(); + float[] zoomScales = imageZoomer.getDoubleClickZoomScales(); zoom(zoomScales[zoomScales.length - 1], x, y, false); } @@ -391,161 +524,55 @@ public void location(float x, float y, boolean animate) { } if (animate) { - locationRunner = new LocationRunner(context, this); + locationRunner = new LocationRunner(imageZoomer, this); locationRunner.location(startX, startY, endX, endY); } else { translateBy(-(endX - startX), -(endY - startY)); } } - /** - * 缩放 - */ - public void zoom(float scale, float focalX, float focalY, boolean animate) { + void zoom(float scale, float focalX, float focalY, boolean animate) { if (animate) { - new ZoomRunner(this, scaleDragGestureListener, getZoomScale(), scale, focalX, focalY).zoom(); + new ZoomRunner(imageZoomer, this, getZoomScale(), scale, focalX, focalY).zoom(); } else { float baseScale = getDefaultZoomScale(); float supportZoomScale = getSupportZoomScale(); float finalScale = scale / baseScale; float addScale = finalScale / supportZoomScale; - supportMatrix.postScale(addScale, addScale, focalX, focalY); - checkAndApplyMatrix(); + scaleBy(addScale, focalX, focalY); } } -// /** -// * 旋转图片(会清除已经有的缩放和移动数据,旋转角度会一直存在) -// */ -// public void rotateDegreesChanged() { -// resetSizes(); -// resetSupportMatrix(); -// resetBaseMatrix(); -// resetZoomScales(); -// checkAndApplyMatrix(); -// } - - - /* -----------可获取信息----------- */ - - - public ImageZoomer getImageZoomer() { - return imageZoomer; - } - - /** - * 获取边界名称,log 专用 - */ - private String getScrollEdgeName(int scrollEdge) { - if (scrollEdge == EDGE_NONE) { - return "NONE"; - } else if (scrollEdge == EDGE_START) { - return "START"; - } else if (scrollEdge == EDGE_END) { - return "END"; - } else if (scrollEdge == EDGE_BOTH) { - return "BOTH"; - } else { - return "UNKNOWN"; - } - } - - /** - * 获取绘制Matrix - */ - public Matrix getDrawMatrix() { - drawMatrix.set(defaultMatrix); + Matrix getDrawMatrix() { + drawMatrix.set(baseMatrix); drawMatrix.postConcat(supportMatrix); return drawMatrix; } - /** - * 获取默认缩放比例 - */ - public float getDefaultZoomScale() { - return SketchUtils.getMatrixScale(defaultMatrix); + float getDefaultZoomScale() { + return SketchUtils.getMatrixScale(baseMatrix); } - /** - * 获取 support 缩放比例 - */ - public float getSupportZoomScale() { + float getSupportZoomScale() { return SketchUtils.getMatrixScale(supportMatrix); } - /** - * 获取最小缩放比例 - */ - @SuppressWarnings("unused") - public float getMinZoomScale() { - return zoomScales.minZoomScale; - } - - /** - * 获取最大缩放比例 - */ - @SuppressWarnings("unused") - public float getMaxZoomScale() { - return zoomScales.maxZoomScale; - } - - /** - * 获取双击缩放比例 - */ - @SuppressWarnings("WeakerAccess") - public float[] getDoubleClickZoomScales() { - return zoomScales.doubleClickZoomScales; - } - - /** - * 获取能够让图片完整显示的缩放比例 - */ - @SuppressWarnings("unused") - public float getFullZoomScale() { - return zoomScales.fullZoomScale; - } - - /** - * 获取能够让图片充满ImageView显示的缩放比例 - */ - @SuppressWarnings("unused") - public float getFillZoomScale() { - return zoomScales.fillZoomScale; - } - - /** - * 获取能够让图片按原图比例一比一显示的缩放比例 - */ - @SuppressWarnings("unused") - public float getOriginZoomScale() { - return zoomScales.originZoomScale; + float getZoomScale() { + return SketchUtils.getMatrixScale(getDrawMatrix()); } - /** - * 正在缩放 - */ - public boolean isZooming() { + boolean isZooming() { return zooming; } - /** - * 设置正在缩放状态 - */ void setZooming(boolean zooming) { this.zooming = zooming; } - /** - * 获取当前缩放比例 - */ - public float getZoomScale() { - return SketchUtils.getMatrixScale(getDrawMatrix()); - } - /** * 获取绘制区域 */ - public void getDrawRect(RectF rectF) { + void getDrawRect(RectF rectF) { if (!imageZoomer.isWorking()) { if (SLog.isLoggable(SLog.LEVEL_VERBOSE | SLog.TYPE_ZOOM)) { SLog.v(NAME, "not working. getDrawRect"); @@ -563,7 +590,7 @@ public void getDrawRect(RectF rectF) { /** * 获取预览图上用户可以看到的区域(不受旋转影响) */ - public void getVisibleRect(Rect rect) { + void getVisibleRect(Rect rect) { if (!imageZoomer.isWorking()) { if (SLog.isLoggable(SLog.LEVEL_VERBOSE | SLog.TYPE_ZOOM)) { SLog.v(NAME, "not working. getVisibleRect"); @@ -625,177 +652,4 @@ public void getVisibleRect(Rect rect) { // 将可见区域转回原始角度 SketchUtils.reverseRotateRect(rect, imageZoomer.getRotateDegrees(), drawableSize); } - - private class OnScaleDragGestureListenerImpl implements OnScaleDragGestureListener { - @Override - public void onDrag(float dx, float dy) { - ImageView imageView = imageZoomer.getImageView(); - if (imageView == null) { - return; - } - - // 缩放中不能拖拽 - if (scaleDragGestureDetector.isScaling()) { - return; - } - - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "drag. dx: %s, dy: %s", dx, dy); - } - - supportMatrix.postTranslate(dx, dy); - checkAndApplyMatrix(); - - if (!imageZoomer.isAllowParentInterceptOnEdge() || scaleDragGestureDetector.isScaling() || disallowParentInterceptTouchEvent) { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "disallow parent intercept touch event. onDrag. allowParentInterceptOnEdge=%s, scaling=%s, tempDisallowParentInterceptTouchEvent=%s", - imageZoomer.isAllowParentInterceptOnEdge(), scaleDragGestureDetector.isScaling(), disallowParentInterceptTouchEvent); - } - requestDisallowInterceptTouchEvent(true); - return; - } - - // 滑动到边缘时父类可以拦截触摸事件,但暂时不处理顶部和底部边界 -// if (horScrollEdge == EDGE_BOTH || (horScrollEdge == EDGE_START && dx >= 1f) || (horScrollEdge == EDGE_END && dx <= -1f) -// || verScrollEdge == EDGE_BOTH || (verScrollEdge == EDGE_START && dy >= 1f) || (verScrollEdge == EDGE_END && dy <= -1f)) { - if (horScrollEdge == EDGE_BOTH || (horScrollEdge == EDGE_START && dx >= 1f) || (horScrollEdge == EDGE_END && dx <= -1f)) { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "allow parent intercept touch event. onDrag. scrollEdge=%s-%s", - getScrollEdgeName(horScrollEdge), getScrollEdgeName(verScrollEdge)); - } - requestDisallowInterceptTouchEvent(false); - } else { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "disallow parent intercept touch event. onDrag. scrollEdge=%s-%s", - getScrollEdgeName(horScrollEdge), getScrollEdgeName(verScrollEdge)); - } - requestDisallowInterceptTouchEvent(true); - } - } - - @Override - public void onFling(float startX, float startY, float velocityX, float velocityY) { - flingTranslateRunner = new FlingTranslateRunner(context, ZoomManager.this); - flingTranslateRunner.fling((int) velocityX, (int) velocityY); - - ImageZoomer.OnDragFlingListener onDragFlingListener = imageZoomer.getOnDragFlingListener(); - if (onDragFlingListener != null) { - onDragFlingListener.onFling(startX, startY, velocityX, velocityY); - } - } - - @Override - public void onScale(float scaleFactor, float focusX, float focusY) { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "scale. scaleFactor: %s, dx: %s, dy: %s", scaleFactor, focusX, focusY); - } - - tempLastScaleFocusX = focusX; - tempLastScaleFocusY = focusY; - - float oldSuppScale = getSupportZoomScale(); - float newSuppScale = oldSuppScale * scaleFactor; - - if (scaleFactor > 1.0f) { - // 放大的时候,如果当前已经超过最大缩放比例,就调慢缩放速度 - // 这样就能模拟出超过最大缩放比例时很难再继续放大有种拉橡皮筋的感觉 - float maxSuppScale = zoomScales.maxZoomScale / SketchUtils.getMatrixScale(defaultMatrix); - if (oldSuppScale >= maxSuppScale) { - float addScale = newSuppScale - oldSuppScale; - addScale *= 0.4; - newSuppScale = oldSuppScale + addScale; - scaleFactor = newSuppScale / oldSuppScale; - } - } else if (scaleFactor < 1.0f) { - // 缩小的时候,如果当前已经小于最小缩放比例,就调慢缩放速度 - // 这样就能模拟出小于最小缩放比例时很难再继续缩小有种拉橡皮筋的感觉 - float minSuppScale = zoomScales.minZoomScale / SketchUtils.getMatrixScale(defaultMatrix); - if (oldSuppScale <= minSuppScale) { - float addScale = newSuppScale - oldSuppScale; - addScale *= 0.4; - newSuppScale = oldSuppScale + addScale; - scaleFactor = newSuppScale / oldSuppScale; - } - } - - supportMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); - checkAndApplyMatrix(); - - ImageZoomer.OnScaleChangeListener onScaleChangeListener = imageZoomer.getOnScaleChangeListener(); - if (onScaleChangeListener != null) { - onScaleChangeListener.onScaleChanged(scaleFactor, focusX, focusY); - } - } - - @Override - public boolean onScaleBegin() { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "scale begin"); - } - - setZooming(true); - return true; - } - - @Override - public void onScaleEnd() { - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "scale end"); - } - - float currentScale = SketchUtils.formatFloat(getZoomScale(), 2); - boolean overMinZoomScale = currentScale < SketchUtils.formatFloat(zoomScales.minZoomScale, 2); - boolean overMaxZoomScale = currentScale > SketchUtils.formatFloat(zoomScales.maxZoomScale, 2); - if (!overMinZoomScale && !overMaxZoomScale) { - setZooming(false); - - List onMatrixChangeListenerList = imageZoomer.getOnMatrixChangeListenerList(); - if (onMatrixChangeListenerList != null && !onMatrixChangeListenerList.isEmpty()) { - for (int w = 0, size = onMatrixChangeListenerList.size(); w < size; w++) { - onMatrixChangeListenerList.get(w).onMatrixChanged(imageZoomer); - } - } - } - } - } - - private class ActionListenerImpl implements ActionListener { - @Override - public void onActionDown(MotionEvent ev) { - tempLastScaleFocusX = 0; - tempLastScaleFocusY = 0; - - // 上来就禁止父View拦截事件 - if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { - SLog.d(NAME, "disallow parent intercept touch event. action down"); - } - requestDisallowInterceptTouchEvent(true); - - // 取消快速滚动 - cancelFling(); - } - - @Override - public void onActionUp(MotionEvent ev) { - float currentScale = SketchUtils.formatFloat(getZoomScale(), 2); - if (currentScale < SketchUtils.formatFloat(zoomScales.minZoomScale, 2)) { - // 如果当前缩放倍数小于最小倍数就回滚至最小倍数 - RectF drawRectF = new RectF(); - getDrawRect(drawRectF); - if (!drawRectF.isEmpty()) { - zoom(zoomScales.minZoomScale, drawRectF.centerX(), drawRectF.centerY(), true); - } - } else if (currentScale > SketchUtils.formatFloat(zoomScales.maxZoomScale, 2)) { - // 如果当前缩放倍数大于最大倍数就回滚至最大倍数 - if (tempLastScaleFocusX != 0 && tempLastScaleFocusY != 0) { - zoom(zoomScales.maxZoomScale, tempLastScaleFocusX, tempLastScaleFocusY, true); - } - } - } - - @Override - public void onActionCancel(MotionEvent ev) { - onActionUp(ev); - } - } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomScales.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/Scales.java similarity index 83% rename from sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomScales.java rename to sketch/src/main/java/me/xiaopan/sketch/zoom/Scales.java index 73898dd428..5b347d865c 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomScales.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/Scales.java @@ -22,7 +22,7 @@ import me.xiaopan.sketch.Sketch; import me.xiaopan.sketch.decode.ImageSizeCalculator; -public class ZoomScales { +public class Scales { private static final float DEFAULT_MAXIMIZE_SCALE = 1.75f; private static final float DEFAULT_MINIMUM_SCALE = 1.0f; @@ -36,15 +36,15 @@ public class ZoomScales { public float originZoomScale; // 能够让图片按照真实尺寸一比一显示的缩放比例 @SuppressWarnings("ConstantConditions") - public void reset(Context context, Size viewSize, Size imageSize, Size drawableSize, ScaleType scaleType, float rotateDegrees, boolean readMode) { - final int drawableWidth = rotateDegrees % 180 == 0 ? drawableSize.getWidth() : drawableSize.getHeight(); - final int drawableHeight = rotateDegrees % 180 == 0 ? drawableSize.getHeight() : drawableSize.getWidth(); - final int imageWidth = rotateDegrees % 180 == 0 ? imageSize.getWidth() : imageSize.getHeight(); - final int imageHeight = rotateDegrees % 180 == 0 ? imageSize.getHeight() : imageSize.getWidth(); - - final float widthScale = (float) viewSize.getWidth() / drawableWidth; - final float heightScale = (float) viewSize.getHeight() / drawableHeight; - final boolean imageThanViewLarge = drawableWidth > viewSize.getWidth() || drawableHeight > viewSize.getHeight(); + public void reset(Context context, Sizes sizes, ScaleType scaleType, float rotateDegrees, boolean readMode) { + final int drawableWidth = rotateDegrees % 180 == 0 ? sizes.drawableSize.getWidth() : sizes.drawableSize.getHeight(); + final int drawableHeight = rotateDegrees % 180 == 0 ? sizes.drawableSize.getHeight() : sizes.drawableSize.getWidth(); + final int imageWidth = rotateDegrees % 180 == 0 ? sizes.imageSize.getWidth() : sizes.imageSize.getHeight(); + final int imageHeight = rotateDegrees % 180 == 0 ? sizes.imageSize.getHeight() : sizes.imageSize.getWidth(); + + final float widthScale = (float) sizes.viewSize.getWidth() / drawableWidth; + final float heightScale = (float) sizes.viewSize.getHeight() / drawableHeight; + final boolean imageThanViewLarge = drawableWidth > sizes.viewSize.getWidth() || drawableHeight > sizes.viewSize.getHeight(); // 小的是完整显示比例,大的是充满比例 fullZoomScale = Math.min(widthScale, heightScale); diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBar.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBarHelper.java similarity index 96% rename from sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBar.java rename to sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBarHelper.java index 4fa9707548..964bfeff83 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBar.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/ScrollBarHelper.java @@ -30,7 +30,7 @@ import me.xiaopan.sketch.SLog; import me.xiaopan.sketch.util.SketchUtils; -class ScrollBar { +class ScrollBarHelper { private ImageZoomer imageZoomer; private Paint scrollBarPaint; @@ -46,7 +46,7 @@ class ScrollBar { private HiddenScrollBarRunner hiddenScrollBarRunner; private FadeScrollBarRunner fadeScrollBarRunner; - ScrollBar(Context context, ImageZoomer imageZoomer) { + ScrollBarHelper(Context context, ImageZoomer imageZoomer) { this.imageZoomer = imageZoomer; scrollBarPaint = new Paint(); @@ -61,7 +61,7 @@ class ScrollBar { fadeScrollBarRunner = new FadeScrollBarRunner(context); } - void drawScrollBar(Canvas canvas) { + void onDraw(Canvas canvas) { final RectF drawRectF = tempDisplayRectF; imageZoomer.getDrawRect(drawRectF); if (drawRectF.isEmpty()) { @@ -116,7 +116,10 @@ void drawScrollBar(Canvas canvas) { } } - void matrixChanged() { + /** + * 此方法里没有执行 imageView.invalidate(),因为回调的地方会有执行 + */ + void onMatrixChanged() { scrollBarPaint.setAlpha(scrollBarAlpha); if (fadeScrollBarRunner.isRunning()) { diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/Sizes.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/Sizes.java new file mode 100644 index 0000000000..499096b066 --- /dev/null +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/Sizes.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 Peng fei Pan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.xiaopan.sketch.zoom; + +import android.graphics.drawable.Drawable; +import android.widget.ImageView; + +import me.xiaopan.sketch.drawable.SketchDrawable; +import me.xiaopan.sketch.drawable.SketchLoadingDrawable; +import me.xiaopan.sketch.util.SketchUtils; + +class Sizes { + Size viewSize = new Size(); // ImageView 尺寸 + Size imageSize = new Size(); // 原始图尺寸 + Size drawableSize = new Size(); // 预览图尺寸 + + void resetSizes(ImageView imageView) { + final int imageViewWidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); + final int imageViewHeight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); + if (imageViewWidth == 0 || imageViewHeight == 0) { + return; + } + + Drawable drawable = SketchUtils.getLastDrawable(imageView.getDrawable()); + if (drawable == null) { + return; + } + + final int drawableWidth = drawable.getIntrinsicWidth(); + final int drawableHeight = drawable.getIntrinsicHeight(); + if (drawableWidth == 0 || drawableHeight == 0) { + return; + } + + viewSize.set(imageViewWidth, imageViewHeight); + drawableSize.set(drawableWidth, drawableHeight); + if (drawable instanceof SketchDrawable && !(drawable instanceof SketchLoadingDrawable)) { + SketchDrawable sketchDrawable = (SketchDrawable) drawable; + imageSize.set(sketchDrawable.getOriginWidth(), sketchDrawable.getOriginHeight()); + } else { + imageSize.set(drawableWidth, drawableHeight); + } + } + + void clean() { + viewSize.set(0, 0); + imageSize.set(0, 0); + drawableSize.set(0, 0); + } + + boolean isEmpty() { + return viewSize.isEmpty() || imageSize.isEmpty() || drawableSize.isEmpty(); + } +} diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/TapListener.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/TapHelper.java similarity index 85% rename from sketch/src/main/java/me/xiaopan/sketch/zoom/TapListener.java rename to sketch/src/main/java/me/xiaopan/sketch/zoom/TapHelper.java index 0da9dcb87a..93acea9ff0 100755 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/TapListener.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/TapHelper.java @@ -16,6 +16,7 @@ package me.xiaopan.sketch.zoom; +import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; @@ -24,12 +25,18 @@ import me.xiaopan.sketch.util.SketchUtils; import me.xiaopan.sketch.viewfun.FunctionCallbackView; -class TapListener extends GestureDetector.SimpleOnGestureListener { +class TapHelper extends GestureDetector.SimpleOnGestureListener { private ImageZoomer imageZoomer; + private GestureDetector tapGestureDetector; - TapListener(ImageZoomer imageZoomer) { + TapHelper(Context appContext, ImageZoomer imageZoomer) { this.imageZoomer = imageZoomer; + this.tapGestureDetector = new GestureDetector(appContext, this); + } + + boolean onTouchEvent(MotionEvent event) { + return tapGestureDetector.onTouchEvent(event); } @Override @@ -40,14 +47,13 @@ public boolean onDown(MotionEvent e) { @Override public boolean onSingleTapConfirmed(MotionEvent e) { ImageView imageView = imageZoomer.getImageView(); - ImageZoomer.OnViewTapListener tapListener = imageZoomer.getOnViewTapListener(); - if (imageView != null && tapListener != null) { + if (tapListener != null) { tapListener.onViewTap(imageView, e.getX(), e.getY()); return true; } - if (imageView != null && imageView instanceof FunctionCallbackView) { + if (imageView instanceof FunctionCallbackView) { FunctionCallbackView functionCallbackView = (FunctionCallbackView) imageView; View.OnClickListener clickListener = functionCallbackView.getOnClickListener(); if (clickListener != null && functionCallbackView.isClickable()) { @@ -64,14 +70,13 @@ public void onLongPress(MotionEvent e) { super.onLongPress(e); ImageView imageView = imageZoomer.getImageView(); - ImageZoomer.OnViewLongPressListener longPressListener = imageZoomer.getOnViewLongPressListener(); - if (imageView != null && longPressListener != null) { + if (longPressListener != null) { longPressListener.onViewLongPress(imageView, e.getX(), e.getY()); return; } - if (imageView != null && imageView instanceof FunctionCallbackView) { + if (imageView instanceof FunctionCallbackView) { FunctionCallbackView functionCallbackView = (FunctionCallbackView) imageView; View.OnLongClickListener longClickListener = functionCallbackView.getOnLongClickListener(); if (longClickListener != null && functionCallbackView.isLongClickable()) { diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomRunner.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomRunner.java index 901626b83d..5de8ad59e4 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomRunner.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/ZoomRunner.java @@ -17,7 +17,7 @@ package me.xiaopan.sketch.zoom; import me.xiaopan.sketch.SLog; -import me.xiaopan.sketch.zoom.gestures.OnScaleDragGestureListener; +import me.xiaopan.sketch.util.SketchUtils; class ZoomRunner implements Runnable { @@ -26,37 +26,38 @@ class ZoomRunner implements Runnable { private final long mStartTime; private final float mZoomStart; private final float mZoomEnd; - private ZoomManager zoomManager; - private OnScaleDragGestureListener scaleDragGestureListener; + + private ImageZoomer imageZoomer; + private ScaleDragHelper scaleDragHelper; - ZoomRunner(ZoomManager zoomManager, OnScaleDragGestureListener scaleDragGestureListener, final float currentZoom, final float targetZoom, final float focalX, final float focalY) { - this.zoomManager = zoomManager; - this.scaleDragGestureListener = scaleDragGestureListener; - mFocalX = focalX; - mFocalY = focalY; - mStartTime = System.currentTimeMillis(); - mZoomStart = currentZoom; - mZoomEnd = targetZoom; + ZoomRunner(ImageZoomer imageZoomer, ScaleDragHelper scaleDragHelper, final float currentZoom, final float targetZoom, final float focalX, final float focalY) { + this.imageZoomer = imageZoomer; + this.scaleDragHelper = scaleDragHelper; + this.mFocalX = focalX; + this.mFocalY = focalY; + this.mStartTime = System.currentTimeMillis(); + this.mZoomStart = currentZoom; + this.mZoomEnd = targetZoom; } @Override public void run() { - if (!zoomManager.getImageZoomer().isWorking()) { + if (!imageZoomer.isWorking()) { SLog.w(ImageZoomer.NAME, "not working. zoom run"); return; } float t = interpolate(); float scale = mZoomStart + t * (mZoomEnd - mZoomStart); - float deltaScale = scale / zoomManager.getZoomScale(); + float deltaScale = scale / scaleDragHelper.getZoomScale(); boolean continueZoom = t < 1f; - zoomManager.setZooming(continueZoom); - scaleDragGestureListener.onScale(deltaScale, mFocalX, mFocalY); + scaleDragHelper.setZooming(continueZoom); + scaleDragHelper.onScale(deltaScale, mFocalX, mFocalY); // We haven't hit our target scale yet, so post ourselves again if (continueZoom) { - CompatUtils.postOnAnimation(zoomManager.getImageZoomer().getImageView(), this); + SketchUtils.postOnAnimation(imageZoomer.getImageView(), this); } else { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_ZOOM)) { SLog.d(ImageZoomer.NAME, "finished. zoom run"); @@ -65,13 +66,13 @@ public void run() { } private float interpolate() { - float t = 1f * (System.currentTimeMillis() - mStartTime) / zoomManager.getImageZoomer().getZoomDuration(); + float t = 1f * (System.currentTimeMillis() - mStartTime) / imageZoomer.getZoomDuration(); t = Math.min(1f, t); - t = zoomManager.getImageZoomer().getZoomInterpolator().getInterpolation(t); + t = imageZoomer.getZoomInterpolator().getInterpolation(t); return t; } public void zoom() { - zoomManager.getImageZoomer().getImageView().post(this); + imageZoomer.getImageView().post(this); } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/gestures/EclairScaleDragGestureDetector.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/gestures/EclairScaleDragGestureDetector.java index b4d08d0b6e..9446aec7c4 100755 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/gestures/EclairScaleDragGestureDetector.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/gestures/EclairScaleDragGestureDetector.java @@ -20,7 +20,7 @@ import android.content.Context; import android.view.MotionEvent; -import me.xiaopan.sketch.zoom.CompatUtils; +import me.xiaopan.sketch.util.SketchUtils; @TargetApi(5) public class EclairScaleDragGestureDetector extends CupcakeScaleDragGestureDetector { @@ -66,7 +66,7 @@ public boolean onTouchEvent(MotionEvent ev) { // Ignore deprecation, ACTION_POINTER_ID_MASK and // ACTION_POINTER_ID_SHIFT has same value and are deprecated // You can have either deprecation or lint target api warning - final int pointerIndex = CompatUtils.getPointerIndex(ev.getAction()); + final int pointerIndex = SketchUtils.getPointerIndex(ev.getAction()); final int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new diff --git a/sketch/src/main/java/me/xiaopan/sketch/zoom/huge/HugeImageViewer.java b/sketch/src/main/java/me/xiaopan/sketch/zoom/huge/HugeImageViewer.java index 98e8cc7e97..e60fa8a06d 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/zoom/huge/HugeImageViewer.java +++ b/sketch/src/main/java/me/xiaopan/sketch/zoom/huge/HugeImageViewer.java @@ -38,6 +38,7 @@ /** * 超级大图片查看器 */ +// TODO: 2017/10/21 改名 ImageShredder // TODO: 2017/5/8 重新规划设计大图查看器的实现,感觉现在的有些乱(初始化,解码,显示分离) public class HugeImageViewer { private static final String NAME = "HugeImageViewer";