package com.gzsll.hupu.widget.photodraweeview;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ScrollerCompat;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.view.DraweeView;
import java.lang.ref.WeakReference;
public class Attacher implements IAttacher, View.OnTouchListener, OnScaleDragGestureListener {
private static final int EDGE_NONE = -1;
private static final int EDGE_LEFT = 0;
private static final int EDGE_RIGHT = 1;
private static final int EDGE_BOTH = 2;
private final float[] mMatrixValues = new float[9];
private final RectF mDisplayRect = new RectF();
private final Interpolator mZoomInterpolator = new AccelerateDecelerateInterpolator();
private float mMinScale = IAttacher.DEFAULT_MIN_SCALE;
private float mMidScale = IAttacher.DEFAULT_MID_SCALE;
private float mMaxScale = IAttacher.DEFAULT_MAX_SCALE;
private long mZoomDuration = IAttacher.ZOOM_DURATION;
private ScaleDragDetector mScaleDragDetector;
private GestureDetectorCompat mGestureDetector;
private boolean mBlockParentIntercept = false;
private boolean mAllowParentInterceptOnEdge = true;
private int mScrollEdge = EDGE_BOTH;
private final Matrix mMatrix = new Matrix();
private int mImageInfoHeight = -1, mImageInfoWidth = -1;
private FlingRunnable mCurrentFlingRunnable;
private WeakReference<DraweeView<GenericDraweeHierarchy>> mDraweeView;
private OnPhotoTapListener mPhotoTapListener;
private OnViewTapListener mViewTapListener;
private View.OnLongClickListener mLongClickListener;
private OnScaleChangeListener mScaleChangeListener;
public Attacher(DraweeView<GenericDraweeHierarchy> draweeView) {
mDraweeView = new WeakReference<>(draweeView);
draweeView.getHierarchy().setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);
draweeView.setOnTouchListener(this);
mScaleDragDetector = new ScaleDragDetector(draweeView.getContext(), this);
mGestureDetector = new GestureDetectorCompat(draweeView.getContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
if (null != mLongClickListener) {
mLongClickListener.onLongClick(getDraweeView());
}
}
});
mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
}
public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
if (newOnDoubleTapListener != null) {
this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
} else {
this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
}
}
@Nullable
public DraweeView<GenericDraweeHierarchy> getDraweeView() {
return mDraweeView.get();
}
@Override
public float getMinimumScale() {
return mMinScale;
}
@Override
public float getMediumScale() {
return mMidScale;
}
@Override
public float getMaximumScale() {
return mMaxScale;
}
@Override
public void setMaximumScale(float maximumScale) {
checkZoomLevels(mMinScale, mMidScale, maximumScale);
mMaxScale = maximumScale;
}
@Override
public void setMediumScale(float mediumScale) {
checkZoomLevels(mMinScale, mediumScale, mMaxScale);
mMidScale = mediumScale;
}
@Override
public void setMinimumScale(float minimumScale) {
checkZoomLevels(minimumScale, mMidScale, mMaxScale);
mMinScale = minimumScale;
}
@Override
public float getScale() {
return (float) Math.sqrt(
(float) Math.pow(getMatrixValue(mMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(
getMatrixValue(mMatrix, Matrix.MSKEW_Y), 2));
}
@Override
public void setScale(float scale) {
setScale(scale, false);
}
@Override
public void setScale(float scale, boolean animate) {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null) {
setScale(scale, (draweeView.getRight()) / 2, (draweeView.getBottom()) / 2, false);
}
}
@Override
public void setScale(float scale, float focalX, float focalY, boolean animate) {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null || scale < mMinScale || scale > mMaxScale) {
return;
}
if (animate) {
draweeView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY));
} else {
mMatrix.setScale(scale, scale, focalX, focalY);
checkMatrixAndInvalidate();
}
}
@Override
public void setZoomTransitionDuration(long duration) {
duration = duration < 0 ? IAttacher.ZOOM_DURATION : duration;
mZoomDuration = duration;
}
@Override
public void setAllowParentInterceptOnEdge(boolean allow) {
mAllowParentInterceptOnEdge = allow;
}
@Override
public void setOnScaleChangeListener(OnScaleChangeListener listener) {
mScaleChangeListener = listener;
}
@Override
public void setOnLongClickListener(View.OnLongClickListener listener) {
mLongClickListener = listener;
}
@Override
public void setOnPhotoTapListener(OnPhotoTapListener listener) {
mPhotoTapListener = listener;
}
@Override
public void setOnViewTapListener(OnViewTapListener listener) {
mViewTapListener = listener;
}
@Override
public OnPhotoTapListener getOnPhotoTapListener() {
return mPhotoTapListener;
}
@Override
public OnViewTapListener getOnViewTapListener() {
return mViewTapListener;
}
@Override
public void update(int imageInfoWidth, int imageInfoHeight) {
mImageInfoWidth = imageInfoWidth;
mImageInfoHeight = imageInfoHeight;
updateBaseMatrix();
}
private static void checkZoomLevels(float minZoom, float midZoom, float maxZoom) {
if (minZoom >= midZoom) {
throw new IllegalArgumentException("MinZoom has to be less than MidZoom");
} else if (midZoom >= maxZoom) {
throw new IllegalArgumentException("MidZoom has to be less than MaxZoom");
}
}
private int getViewWidth() {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null) {
return draweeView.getWidth() - draweeView.getPaddingLeft() - draweeView.getPaddingRight();
}
return 0;
}
private int getViewHeight() {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null) {
return draweeView.getHeight() - draweeView.getPaddingTop() - draweeView.getPaddingBottom();
}
return 0;
}
private float getMatrixValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
public Matrix getDrawMatrix() {
return mMatrix;
}
public RectF getDisplayRect() {
checkMatrixBounds();
return getDisplayRect(getDrawMatrix());
}
public void checkMatrixAndInvalidate() {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null) {
return;
}
if (checkMatrixBounds()) {
draweeView.invalidate();
}
}
public boolean checkMatrixBounds() {
RectF rect = getDisplayRect(getDrawMatrix());
if (rect == null) {
return false;
}
float height = rect.height();
float width = rect.width();
float deltaX = 0.0F;
float deltaY = 0.0F;
int viewHeight = getViewHeight();
if (height <= (float) viewHeight) {
deltaY = (viewHeight - height) / 2 - rect.top;
} else if (rect.top > 0.0F) {
deltaY = -rect.top;
} else if (rect.bottom < (float) viewHeight) {
deltaY = viewHeight - rect.bottom;
}
int viewWidth = getViewWidth();
if (width <= viewWidth) {
deltaX = (viewWidth - width) / 2 - rect.left;
mScrollEdge = EDGE_BOTH;
} else if (rect.left > 0) {
deltaX = -rect.left;
mScrollEdge = EDGE_LEFT;
} else if (rect.right < viewWidth) {
deltaX = viewWidth - rect.right;
mScrollEdge = EDGE_RIGHT;
} else {
mScrollEdge = EDGE_NONE;
}
mMatrix.postTranslate(deltaX, deltaY);
return true;
}
private RectF getDisplayRect(Matrix matrix) {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null || (mImageInfoWidth == -1 && mImageInfoHeight == -1)) {
return null;
}
mDisplayRect.set(0.0F, 0.0F, mImageInfoWidth, mImageInfoHeight);
draweeView.getHierarchy().getActualImageBounds(mDisplayRect);
matrix.mapRect(mDisplayRect);
return mDisplayRect;
}
private void updateBaseMatrix() {
if (mImageInfoWidth == -1 && mImageInfoHeight == -1) {
return;
}
resetMatrix();
}
private void resetMatrix() {
mMatrix.reset();
checkMatrixBounds();
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null) {
draweeView.invalidate();
}
}
private void checkMinScale() {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null) {
return;
}
if (getScale() < mMinScale) {
RectF rect = getDisplayRect();
if (null != rect) {
draweeView.post(
new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY()));
}
}
}
@Override
public void onScale(float scaleFactor, float focusX, float focusY) {
if (getScale() < mMaxScale || scaleFactor < 1.0F) {
if (mScaleChangeListener != null) {
mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
}
mMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
checkMatrixAndInvalidate();
}
}
@Override
public void onScaleEnd() {
checkMinScale();
}
@Override
public void onDrag(float dx, float dy) {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null && !mScaleDragDetector.isScaling()) {
mMatrix.postTranslate(dx, dy);
checkMatrixAndInvalidate();
ViewParent parent = draweeView.getParent();
if (parent == null) {
return;
}
if (mAllowParentInterceptOnEdge
&& !mScaleDragDetector.isScaling()
&& !mBlockParentIntercept) {
if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f) || (mScrollEdge
== EDGE_RIGHT && dx <= -1f)) {
parent.requestDisallowInterceptTouchEvent(false);
}
} else {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
@Override
public void onFling(float startX, float startY, float velocityX, float velocityY) {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null) {
return;
}
mCurrentFlingRunnable = new FlingRunnable(draweeView.getContext());
mCurrentFlingRunnable.fling(getViewWidth(), getViewHeight(), (int) velocityX, (int) velocityY);
draweeView.post(mCurrentFlingRunnable);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
ViewParent parent = v.getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
cancelFling();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
ViewParent parent = v.getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
}
break;
}
boolean wasScaling = mScaleDragDetector.isScaling();
boolean wasDragging = mScaleDragDetector.isDragging();
boolean handled = mScaleDragDetector.onTouchEvent(event);
boolean noScale = !wasScaling && !mScaleDragDetector.isScaling();
boolean noDrag = !wasDragging && !mScaleDragDetector.isDragging();
mBlockParentIntercept = noScale && noDrag;
if (mGestureDetector.onTouchEvent(event)) {
handled = true;
}
return handled;
}
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;
mFocalY = focalY;
mStartTime = System.currentTimeMillis();
mZoomStart = currentZoom;
mZoomEnd = targetZoom;
}
@Override
public void run() {
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView == null) {
return;
}
float t = interpolate();
float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
float deltaScale = scale / getScale();
onScale(deltaScale, mFocalX, mFocalY);
if (t < 1f) {
postOnAnimation(draweeView, this);
}
}
private float interpolate() {
float t = 1f * (System.currentTimeMillis() - mStartTime) / mZoomDuration;
t = Math.min(1f, t);
t = mZoomInterpolator.getInterpolation(t);
return t;
}
}
private class FlingRunnable implements Runnable {
private final ScrollerCompat mScroller;
private int mCurrentX, mCurrentY;
public FlingRunnable(Context context) {
mScroller = ScrollerCompat.create(context);
}
public void cancelFling() {
mScroller.abortAnimation();
}
public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) {
final RectF rect = getDisplayRect();
if (null == rect) {
return;
}
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);
} 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 (startX != maxX || startY != maxY) {
mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
}
@Override
public void run() {
if (mScroller.isFinished()) {
return;
}
DraweeView<GenericDraweeHierarchy> draweeView = getDraweeView();
if (draweeView != null && mScroller.computeScrollOffset()) {
final int newX = mScroller.getCurrX();
final int newY = mScroller.getCurrY();
mMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
draweeView.invalidate();
mCurrentX = newX;
mCurrentY = newY;
postOnAnimation(draweeView, this);
}
}
}
private void cancelFling() {
if (mCurrentFlingRunnable != null) {
mCurrentFlingRunnable.cancelFling();
mCurrentFlingRunnable = null;
}
}
private void postOnAnimation(View view, Runnable runnable) {
if (Build.VERSION.SDK_INT >= 16) {
view.postOnAnimation(runnable);
} else {
view.postDelayed(runnable, 16L);
}
}
protected void onDetachedFromWindow() {
cancelFling();
}
}