package com.yuzhi.fine.ui.photoview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.OverScroller;
import android.widget.Scroller;
/**
* Created by liuheng on 2015/6/21.
* <p/>
* 如有任何意见和建议可邮件 bmme@vip.qq.com
*/
public class PhotoView extends ImageView {
private final static int ANIMA_DURING = 300;
private final static float MAX_SCALE = 2.5f;
private int MAX_OVER_SCROLL = 0;
private int MAX_FLING_OVER_SCROLL = 0;
private int MAX_OVER_RESISTANCE = 0;
private Matrix mBaseMatrix = new Matrix();
private Matrix mAnimaMatrix = new Matrix();
private Matrix mSynthesisMatrix = new Matrix();
private Matrix mTmpMatrix = new Matrix();
private GestureDetector mDetector;
private ScaleGestureDetector mScaleDetector;
private OnClickListener mClickListener;
private ScaleType mScaleType;
private boolean hasMultiTouch;
private boolean hasDrawable;
private boolean isKnowSize;
private boolean hasOverTranslate;
private boolean isEnable = false;
private boolean isInit;
private boolean imgLargeWidth;
private boolean imgLargeHeight;
private float mScale = 1.0f;
private int mTranslateX;
private int mTranslateY;
private RectF mWidgetRect = new RectF();
private RectF mBaseRect = new RectF();
private RectF mImgRect = new RectF();
private RectF mTmpRect = new RectF();
private RectF mCommonRect = new RectF();
private PointF mScreenCenter = new PointF();
private PointF mDoubleTab = new PointF();
private Transform mTranslate = new Transform();
private RectF mClip;
private Info mInfo;
private Info mOldInfo;
private Runnable mCompleteCallBack;
private float[] mValues = new float[16];
public PhotoView(Context context) {
super(context);
init();
}
public PhotoView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PhotoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
super.setScaleType(ScaleType.MATRIX);
if (mScaleType == null) mScaleType = ScaleType.CENTER_INSIDE;
mDetector = new GestureDetector(getContext(), mGestureListener);
mScaleDetector = new ScaleGestureDetector(getContext(), mScaleListener);
float density = getResources().getDisplayMetrics().density;
MAX_OVER_SCROLL = (int) (density * 30);
MAX_FLING_OVER_SCROLL = (int) (density * 30);
MAX_OVER_RESISTANCE = (int) (density * 140);
}
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
mClickListener = l;
}
@Override
public void setScaleType(ScaleType scaleType) {
ScaleType old = mScaleType;
mScaleType = scaleType;
if (old != scaleType)
initBase();
}
public void enable() {
isEnable = true;
}
public void disenable() {
isEnable = false;
}
@Override
public void setImageResource(int resId) {
setImageDrawable(getResources().getDrawable(resId));
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if (drawable == null) return;
if (drawable.getIntrinsicHeight() <= 0 || drawable.getIntrinsicWidth() <= 0)
return;
if (!hasDrawable) {
hasDrawable = true;
}
initBase();
}
private void initBase() {
if (!hasDrawable) return;
if (!isKnowSize) return;
mBaseMatrix.reset();
mAnimaMatrix.reset();
Drawable img = getDrawable();
int w = getWidth();
int h = getHeight();
int imgw = img.getIntrinsicWidth();
int imgh = img.getIntrinsicHeight();
mBaseRect.set(0, 0, imgw, imgh);
// 以图片中心点居中位移
int tx = (w - imgw) / 2;
int ty = (h - imgh) / 2;
float sx = 1;
float sy = 1;
// 缩放,默认不超过屏幕大小
if (imgw > w) {
sx = (float) w / imgw;
}
if (imgh > h) {
sy = (float) h / imgh;
}
float scale = sx < sy ? sx : sy;
mBaseMatrix.reset();
mBaseMatrix.postTranslate(tx, ty);
mBaseMatrix.postScale(scale, scale, mScreenCenter.x, mScreenCenter.y);
mBaseMatrix.mapRect(mBaseRect);
mDoubleTab.set(mScreenCenter);
executeTranslate();
switch (mScaleType) {
case CENTER:
initCenter();
break;
case CENTER_CROP:
initCenterCrop();
break;
case CENTER_INSIDE:
initCenterInside();
break;
case FIT_CENTER:
initFitCenter();
break;
case FIT_START:
initFitStart();
break;
case FIT_END:
initFitEnd();
break;
case FIT_XY:
initFitXY();
break;
}
isInit = true;
if (mInfo != null) {
animaFrom(mInfo);
mOldInfo = mInfo;
mInfo = null;
}
}
private void initCenter() {
if (!hasDrawable) return;
if (!isKnowSize) return;
Drawable img = getDrawable();
int imgw = img.getIntrinsicWidth();
int imgh = img.getIntrinsicHeight();
if (imgw > mWidgetRect.width() || imgh > mWidgetRect.height()) {
float scaleX = imgw / mImgRect.width();
float scaleY = imgh / mImgRect.height();
mScale = scaleX > scaleY ? scaleX : scaleY;
mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
executeTranslate();
}
}
private void initCenterCrop() {
if (mImgRect.width() < mWidgetRect.width() || mImgRect.height() < mWidgetRect.height()) {
float scaleX = mWidgetRect.width() / mImgRect.width();
float scaleY = mWidgetRect.height() / mImgRect.height();
mScale = scaleX > scaleY ? scaleX : scaleY;
mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
executeTranslate();
}
}
private void initCenterInside() {
if (mImgRect.width() > mWidgetRect.width() || mImgRect.height() > mWidgetRect.height()) {
float scaleX = mWidgetRect.width() / mImgRect.width();
float scaleY = mWidgetRect.height() / mImgRect.height();
mScale = scaleX < scaleY ? scaleX : scaleY;
mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
executeTranslate();
}
}
private void initFitCenter() {
if (mImgRect.width() < mWidgetRect.width()) {
mScale = mWidgetRect.width() / mImgRect.width();
mAnimaMatrix.postScale(mScale, mScale, mScreenCenter.x, mScreenCenter.y);
executeTranslate();
}
}
private void initFitStart() {
initFitCenter();
float ty = -mImgRect.top;
mTranslateY += ty;
mAnimaMatrix.postTranslate(0, ty);
executeTranslate();
}
private void initFitEnd() {
initFitCenter();
float ty = (mWidgetRect.bottom - mImgRect.bottom);
mTranslateY += ty;
mAnimaMatrix.postTranslate(0, ty);
executeTranslate();
}
private void initFitXY() {
float scaleX = mWidgetRect.width() / mImgRect.width();
float scaleY = mWidgetRect.height() / mImgRect.height();
mAnimaMatrix.postScale(scaleX, scaleY, mScreenCenter.x, mScreenCenter.y);
executeTranslate();
}
private void executeTranslate() {
mSynthesisMatrix.set(mBaseMatrix);
mSynthesisMatrix.postConcat(mAnimaMatrix);
setImageMatrix(mSynthesisMatrix);
mAnimaMatrix.mapRect(mImgRect, mBaseRect);
imgLargeWidth = mImgRect.width() > mWidgetRect.width();
imgLargeHeight = mImgRect.height() > mWidgetRect.height();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidgetRect.set(0, 0, w, h);
mScreenCenter.set(w / 2, h / 2);
if (!isKnowSize) {
isKnowSize = true;
initBase();
}
}
@Override
public void draw(Canvas canvas) {
if (mClip != null) {
canvas.clipRect(mClip);
mClip = null;
}
super.draw(canvas);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (isEnable) {
mDetector.onTouchEvent(event);
mScaleDetector.onTouchEvent(event);
final int Action = event.getAction();
if (Action == MotionEvent.ACTION_UP || Action == MotionEvent.ACTION_CANCEL) onUp(event);
} else {
return super.dispatchTouchEvent(event);
}
return true;
}
private void onUp(MotionEvent ev) {
if (mTranslate.isRuning) return;
float scale = mScale;
if (mScale < 1) {
scale = 1;
mTranslate.withScale(mScale, 1);
} else if (mScale > MAX_SCALE) {
scale = MAX_SCALE;
mTranslate.withScale(mScale, MAX_SCALE);
}
mAnimaMatrix.getValues(mValues);
float s = mValues[Matrix.MSCALE_X];
float tx = mValues[Matrix.MTRANS_X];
float ty = mValues[Matrix.MTRANS_Y];
float cpx = tx - mTranslateX;
float cpy = ty - mTranslateY;
mDoubleTab.x = -cpx / (s - 1);
mDoubleTab.y = -cpy / (s - 1);
mTmpRect.set(mImgRect);
if (scale != mScale) {
mTmpMatrix.setScale(scale, scale, mDoubleTab.x, mDoubleTab.y);
mTmpMatrix.postTranslate(mTranslateX, mTranslateY);
mTmpMatrix.mapRect(mTmpRect, mBaseRect);
}
doTranslateReset(mTmpRect);
mTranslate.start();
}
private void doTranslateReset(RectF imgRect) {
int tx = 0;
int ty = 0;
if (imgRect.width() < mWidgetRect.width()) {
if (!isImageCenterWidth())
tx = -(int) ((mWidgetRect.width() - imgRect.width()) / 2 - imgRect.left);
} else {
if (imgRect.left > mWidgetRect.left) {
tx = (int) (imgRect.left - mWidgetRect.left);
} else if (imgRect.right < mWidgetRect.right) {
tx = (int) (imgRect.right - mWidgetRect.right);
}
}
if (imgRect.height() < mWidgetRect.height()) {
if (!isImageCenterHeight())
ty = -(int) ((mWidgetRect.height() - imgRect.height()) / 2 - imgRect.top);
} else {
if (imgRect.top > mWidgetRect.top) {
ty = (int) (imgRect.top - mWidgetRect.top);
} else if (imgRect.bottom < mWidgetRect.bottom) {
ty = (int) (imgRect.bottom - mWidgetRect.bottom);
}
}
if (tx != 0 || ty != 0) {
if (!mTranslate.mFlingScroller.isFinished()) mTranslate.mFlingScroller.abortAnimation();
mTranslate.withTranslate(mTranslateX, mTranslateY, -tx, -ty);
}
}
private boolean isImageCenterHeight() {
return Math.round(mImgRect.top) == (mWidgetRect.height() - mImgRect.height()) / 2;
}
private boolean isImageCenterWidth() {
return Math.round(mImgRect.left) == (mWidgetRect.width() - mImgRect.width()) / 2;
}
private ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
return false;
mScale *= scaleFactor;
mDoubleTab.set(detector.getFocusX(), detector.getFocusY());
mAnimaMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
executeTranslate();
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
hasMultiTouch = true;
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
};
private float resistanceScrollByX(float overScroll, float detalX) {
float s = detalX * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
return s;
}
private float resistanceScrollByY(float overScroll, float detalY) {
float s = detalY * (Math.abs(Math.abs(overScroll) - MAX_OVER_RESISTANCE) / (float) MAX_OVER_RESISTANCE);
return s;
}
/**
* 匹配两个Rect的共同部分输出到out,若无共同部分则输出0,0,0,0
*/
private void mapRect(RectF r1, RectF r2, RectF out) {
float l, r, t, b;
l = r1.left > r2.left ? r1.left : r2.left;
r = r1.right < r2.right ? r1.right : r2.right;
if (l > r) {
out.set(0, 0, 0, 0);
return;
}
t = r1.top > r2.top ? r1.top : r2.top;
b = r1.bottom < r2.bottom ? r1.bottom : r2.bottom;
if (t > b) {
out.set(0, 0, 0, 0);
return;
}
out.set(l, t, r, b);
}
private void checkRect() {
if (!hasOverTranslate) {
mapRect(mWidgetRect, mImgRect, mCommonRect);
}
}
private Runnable mClickRunnable = new Runnable() {
@Override
public void run() {
if (mClickListener != null) {
mClickListener.onClick(PhotoView.this);
}
}
};
private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
hasOverTranslate = false;
hasMultiTouch = false;
removeCallbacks(mClickRunnable);
return super.onDown(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (hasMultiTouch) return false;
if (!imgLargeWidth && !imgLargeHeight) return false;
if (!mTranslate.mFlingScroller.isFinished()) return false;
float vx = velocityX;
float vy = velocityY;
if (Math.round(mImgRect.left) >= mWidgetRect.left || Math.round(mImgRect.right) <= mWidgetRect.right) {
vx = 0;
}
if (Math.round(mImgRect.top) >= mWidgetRect.top || Math.round(mImgRect.bottom) <= mWidgetRect.bottom) {
vy = 0;
}
doTranslateReset(mImgRect);
mTranslate.withFling(vx, vy);
mTranslate.start();
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mTranslate.isRuning) {
mTranslate.stop();
}
if (canScrollHorizontallySelf(distanceX)) {
if (distanceX < 0 && mImgRect.left - distanceX > mWidgetRect.left)
distanceX = mImgRect.left;
if (distanceX > 0 && mImgRect.right - distanceX < mWidgetRect.right)
distanceX = mImgRect.right - mWidgetRect.right;
mAnimaMatrix.postTranslate(-distanceX, 0);
mTranslateX -= distanceX;
} else if (imgLargeWidth || hasMultiTouch || hasOverTranslate) {
checkRect();
if (!hasMultiTouch) {
if (distanceX < 0 && mImgRect.left - distanceX > mCommonRect.left)
distanceX = resistanceScrollByX(mImgRect.left - mCommonRect.left, distanceX);
if (distanceX > 0 && mImgRect.right - distanceX < mCommonRect.right)
distanceX = resistanceScrollByX(mImgRect.right - mCommonRect.right, distanceX);
}
mTranslateX -= distanceX;
mAnimaMatrix.postTranslate(-distanceX, 0);
hasOverTranslate = true;
}
if (canScrollVerticallySelf(distanceY)) {
if (distanceY < 0 && mImgRect.top - distanceY > mWidgetRect.top)
distanceY = mImgRect.top;
if (distanceY > 0 && mImgRect.bottom - distanceY < mWidgetRect.bottom)
distanceY = mImgRect.bottom - mWidgetRect.bottom;
mAnimaMatrix.postTranslate(0, -distanceY);
mTranslateY -= distanceY;
} else if (imgLargeHeight || hasOverTranslate || hasMultiTouch) {
checkRect();
if (!hasMultiTouch) {
if (distanceY < 0 && mImgRect.top - distanceY > mCommonRect.top)
distanceY = resistanceScrollByY(mImgRect.top - mCommonRect.top, distanceY);
if (distanceY > 0 && mImgRect.bottom - distanceY < mCommonRect.bottom)
distanceY = resistanceScrollByY(mImgRect.bottom - mCommonRect.bottom, distanceY);
}
mAnimaMatrix.postTranslate(0, -distanceY);
mTranslateY -= distanceY;
hasOverTranslate = true;
}
executeTranslate();
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
postDelayed(mClickRunnable, 250);
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
mTranslate.stop();
float from = 1;
float to = 1;
if (mScale == 1) {
from = 1;
to = MAX_SCALE;
if (mImgRect.width() < mWidgetRect.width())
mDoubleTab.set(mScreenCenter.x, mScreenCenter.y);
else
mDoubleTab.set(e.getX(), mScreenCenter.y);
} else {
from = mScale;
to = 1;
mTranslate.withTranslate(mTranslateX, mTranslateY, -mTranslateX, -mTranslateY);
}
mTranslate.withScale(from, to);
mTranslate.start();
return false;
}
};
public boolean canScrollHorizontallySelf(float direction) {
if (mImgRect.width() <= mWidgetRect.width()) return false;
if (direction < 0 && Math.round(mImgRect.left) - direction >= mWidgetRect.left)
return false;
if (direction > 0 && Math.round(mImgRect.right) - direction <= mWidgetRect.right)
return false;
return true;
}
public boolean canScrollVerticallySelf(float direction) {
if (mImgRect.height() <= mWidgetRect.height()) return false;
if (direction < 0 && Math.round(mImgRect.top) - direction >= mWidgetRect.top) return false;
if (direction > 0 && Math.round(mImgRect.bottom) - direction <= mWidgetRect.bottom)
return false;
return true;
}
@Override
public boolean canScrollHorizontally(int direction) {
if (hasMultiTouch) return true;
return canScrollHorizontallySelf(direction);
}
@Override
public boolean canScrollVertically(int direction) {
if (hasMultiTouch) return true;
return canScrollVerticallySelf(direction);
}
private class Transform implements Runnable {
boolean isRuning;
OverScroller mTranslateScroller;
OverScroller mFlingScroller;
Scroller mScaleScroller;
Scroller mClipScroll;
ClipCalculate C;
int mLastFlingX;
int mLastFlingY;
int mLastTranslateX;
int mLastTranslateY;
RectF mClipRect = new RectF();
Transform() {
Context ctx = getContext();
DecelerateInterpolator i = new DecelerateInterpolator();
mTranslateScroller = new OverScroller(ctx, i);
mScaleScroller = new Scroller(ctx, i);
mFlingScroller = new OverScroller(ctx, i);
mClipScroll = new Scroller(ctx, i);
}
void withTranslate(int startX, int startY, int deltaX, int deltaY) {
mLastTranslateX = 0;
mLastTranslateY = 0;
mTranslateScroller.startScroll(0, 0, deltaX, deltaY, ANIMA_DURING);
}
void withScale(float form, float to) {
mScaleScroller.startScroll((int) (form * 10000), 0, (int) ((to - form) * 10000), 0, ANIMA_DURING);
}
void withClip(float fromX, float fromY, float deltaX, float deltaY, int d, ClipCalculate c) {
mClipScroll.startScroll((int) (fromX * 10000), (int) (fromY * 10000), (int) (deltaX * 10000), (int) (deltaY * 10000), d);
C = c;
}
void withFling(float velocityX, float velocityY) {
mLastFlingX = velocityX < 0 ? Integer.MAX_VALUE : 0;
int distanceX = (int) (velocityX > 0 ? Math.abs(mImgRect.left) : mImgRect.right - mWidgetRect.right);
distanceX = velocityX < 0 ? Integer.MAX_VALUE - distanceX : distanceX;
int minX = velocityX < 0 ? distanceX : 0;
int maxX = velocityX < 0 ? Integer.MAX_VALUE : distanceX;
int overX = velocityX < 0 ? Integer.MAX_VALUE - minX : distanceX;
mLastFlingY = velocityY < 0 ? Integer.MAX_VALUE : 0;
int distanceY = (int) (velocityY > 0 ? Math.abs(mImgRect.top) : mImgRect.bottom - mWidgetRect.bottom);
distanceY = velocityY < 0 ? Integer.MAX_VALUE - distanceY : distanceY;
int minY = velocityY < 0 ? distanceY : 0;
int maxY = velocityY < 0 ? Integer.MAX_VALUE : distanceY;
int overY = velocityY < 0 ? Integer.MAX_VALUE - minY : distanceY;
if (velocityX == 0) {
maxX = 0;
minX = 0;
}
if (velocityY == 0) {
maxY = 0;
minY = 0;
}
mFlingScroller.fling(mLastFlingX, mLastFlingY, (int) velocityX, (int) velocityY, minX, maxX, minY, maxY, Math.abs(overX) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL, Math.abs(overY) < MAX_FLING_OVER_SCROLL * 2 ? 0 : MAX_FLING_OVER_SCROLL);
}
void start() {
isRuning = true;
post(this);
}
void stop() {
removeCallbacks(this);
mTranslateScroller.abortAnimation();
mScaleScroller.abortAnimation();
mFlingScroller.abortAnimation();
isRuning = false;
}
@Override
public void run() {
if (!isRuning) return;
boolean endAnima = true;
if (mScaleScroller.computeScrollOffset()) {
mScale = mScaleScroller.getCurrX() / 10000f;
endAnima = false;
}
if (mTranslateScroller.computeScrollOffset()) {
int tx = mTranslateScroller.getCurrX() - mLastTranslateX;
int ty = mTranslateScroller.getCurrY() - mLastTranslateY;
mTranslateX += tx;
mTranslateY += ty;
mLastTranslateX = mTranslateScroller.getCurrX();
mLastTranslateY = mTranslateScroller.getCurrY();
endAnima = false;
}
if (mFlingScroller.computeScrollOffset()) {
int x = mFlingScroller.getCurrX() - mLastFlingX;
int y = mFlingScroller.getCurrY() - mLastFlingY;
mLastFlingX = mFlingScroller.getCurrX();
mLastFlingY = mFlingScroller.getCurrY();
mTranslateX += x;
mTranslateY += y;
endAnima = false;
}
if (mClipScroll.computeScrollOffset() || mClip != null) {
float sx = mClipScroll.getCurrX() / 10000f;
float sy = mClipScroll.getCurrY() / 10000f;
mTmpMatrix.setScale(sx, sy, (mImgRect.left + mImgRect.right) / 2, C.calculateTop());
mTmpMatrix.mapRect(mClipRect, mImgRect);
if (sx == 1) {
mClipRect.left = mWidgetRect.left;
mClipRect.right = mWidgetRect.right;
}
if (sy == 1) {
mClipRect.top = mWidgetRect.top;
mClipRect.bottom = mWidgetRect.bottom;
}
mClip = mClipRect;
}
if (!endAnima) {
mAnimaMatrix.reset();
mAnimaMatrix.postScale(mScale, mScale, mDoubleTab.x, mDoubleTab.y);
mAnimaMatrix.postTranslate(mTranslateX, mTranslateY);
executeTranslate();
post(this);
} else {
isRuning = false;
invalidate();
if (mCompleteCallBack != null) {
mCompleteCallBack.run();
mCompleteCallBack = null;
}
}
}
}
public Info getInfo() {
RectF rect = new RectF();
RectF local = new RectF();
int[] p = new int[2];
getLocationInWindow(p);
rect.set(p[0] + mImgRect.left, p[1] + mImgRect.top, p[0] + mImgRect.right, p[1] + mImgRect.bottom);
local.set(p[0], p[1], p[0] + mImgRect.width(), p[1] + mImgRect.height());
return new Info(rect, local, mImgRect, mWidgetRect, mScale, mScaleType);
}
private void reset() {
mAnimaMatrix.reset();
executeTranslate();
mScale = 1;
mTranslateX = 0;
mTranslateY = 0;
}
public interface ClipCalculate {
float calculateTop();
}
public class START implements ClipCalculate {
public float calculateTop() {
return mImgRect.top;
}
}
public class END implements ClipCalculate {
public float calculateTop() {
return mImgRect.bottom;
}
}
public class OTHER implements ClipCalculate {
public float calculateTop() {
return (mImgRect.top + mImgRect.bottom) / 2;
}
}
public void animaFrom(Info info) {
if (isInit) {
reset();
Info mine = getInfo();
float scaleX = info.mImgRect.width() / mine.mImgRect.width();
float scaleY = info.mImgRect.height() / mine.mImgRect.height();
float scale = scaleX < scaleY ? scaleX : scaleY;
float tx = info.mRect.left - mine.mRect.left;
float ty = info.mRect.top - mine.mRect.top;
mAnimaMatrix.reset();
mAnimaMatrix.postScale(scale, scale, mImgRect.left, mImgRect.top);
mAnimaMatrix.postTranslate(tx, ty);
executeTranslate();
mTranslateX += tx;
mTranslateY += ty;
mDoubleTab.x = mImgRect.left - tx;
mDoubleTab.y = mImgRect.top - ty;
mAnimaMatrix.getValues(mValues);
float s = mValues[Matrix.MSCALE_X];
mTranslate.withScale(s, mScale);
mTranslate.withTranslate(mTranslateX, mTranslateY, (int) -tx, (int) -ty);
if (info.mWidgetRect.width() < info.mImgRect.width() || info.mWidgetRect.height() < info.mImgRect.height()) {
float clipX = info.mWidgetRect.width() / info.mImgRect.width();
float clipY = info.mWidgetRect.height() / info.mImgRect.height();
clipX = clipX > 1 ? 1 : clipX;
clipY = clipY > 1 ? 1 : clipY;
ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
mTranslate.withClip(clipX, clipY, 1 - clipX, 1 - clipY, ANIMA_DURING / 3, c);
mTmpMatrix.setScale(clipX, clipY, (mImgRect.left + mImgRect.right) / 2, c.calculateTop());
mTmpMatrix.mapRect(mTranslate.mClipRect, mImgRect);
mClip = mTranslate.mClipRect;
}
mTranslate.start();
} else {
mInfo = info;
}
}
public void animaTo(Info info, Runnable completeCallBack) {
if (isInit) {
executeTranslate();
Info mine = getInfo();
mDoubleTab.x = 0;
mDoubleTab.y = 0;
mAnimaMatrix.getValues(mValues);
mScale = mValues[Matrix.MSCALE_X];
mTranslateX = (int) mValues[Matrix.MTRANS_X];
mTranslateY = (int) mValues[Matrix.MTRANS_Y];
// 缩放
float scaleX = info.mImgRect.width() / mine.mImgRect.width();
float scaleY = info.mImgRect.height() / mine.mImgRect.height();
float scale = scaleX > scaleY ? scaleX : scaleY;
mTmpMatrix.set(mAnimaMatrix);
mTmpMatrix.postScale(scale, scale, mDoubleTab.x, mDoubleTab.y);
mTmpMatrix.getValues(mValues);
scale = mValues[Matrix.MSCALE_X];
mBaseMatrix.getValues(mValues);
int tx = (int) (info.mLocalRect.left - mine.mLocalRect.left + info.mImgRect.left - mValues[Matrix.MTRANS_X] * scale);
int ty = (int) (info.mLocalRect.top - mine.mLocalRect.top + info.mImgRect.top - mValues[Matrix.MTRANS_Y] * scale);
mTranslate.withScale(mScale, scale);
mTranslate.withTranslate(mTranslateX, mTranslateY, -mTranslateX + tx, -mTranslateY + ty);
if (info.mWidgetRect.width() < info.mRect.width() || info.mWidgetRect.height() < info.mRect.height()) {
float clipX = info.mWidgetRect.width() / info.mRect.width();
float clipY = info.mWidgetRect.height() / info.mRect.height();
clipX = clipX > 1 ? 1 : clipX;
clipY = clipY > 1 ? 1 : clipY;
final float cx = clipX;
final float cy = clipY;
final ClipCalculate c = info.mScaleType == ScaleType.FIT_START ? new START() : info.mScaleType == ScaleType.FIT_END ? new END() : new OTHER();
postDelayed(new Runnable() {
@Override
public void run() {
mTranslate.withClip(1, 1, -1 + cx, -1 + cy, ANIMA_DURING / 2, c);
}
}, ANIMA_DURING / 2);
}
mCompleteCallBack = completeCallBack;
mTranslate.start();
}
}
}