效果圖:
Github鏈接:https://github.com/boycy815/PinchImageView
使用:
1:自定義控件通過java代碼:PinchImageView imageView =new PinchImageView(this);獲得image對象餐塘,
2:直接在xml中調用下面的類,自定義控件的用法叮趴;
工具類:
package com.lzyi.tpm.utils;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* 手勢圖片控件
*
* @author clifford
*/
public class PinchImageViewextends ImageView? {
////////////////////////////////配置參數(shù)////////////////////////////////
? ? /**
? ? * 圖片縮放動畫時間
? ? */
? ? public static final int SCALE_ANIMATOR_DURATION =200;
? ? /**
? ? * 慣性動畫衰減參數(shù)
? ? */
? ? public static final float FLING_DAMPING_FACTOR =0.9f;
? ? /**
? ? * 圖片最大放大比例
? ? */
? ? private static final float MAX_SCALE =4f;
? ? ////////////////////////////////監(jiān)聽器////////////////////////////////
? ? /**
? ? * 外界點擊事件
? ? *
? ? * @see #setOnClickListener(OnClickListener)
*/
? ? private OnClickListenermOnClickListener;
? ? /**
? ? * 外界長按事件
? ? *
? ? * @see #setOnLongClickListener(OnLongClickListener)
*/
? ? private OnLongClickListenermOnLongClickListener;
? ? @Override
? ? public void setOnClickListener(OnClickListener l) {
//默認的click會在任何點擊情況下都會觸發(fā)样眠,所以搞成自己的
? ? ? ? mOnClickListener = l;
? ? }
@Override
? ? public void setOnLongClickListener(OnLongClickListener l) {
//默認的long click會在任何長按情況下都會觸發(fā)友瘤,所以搞成自己的
? ? ? ? mOnLongClickListener = l;
? ? }
////////////////////////////////公共狀態(tài)獲取////////////////////////////////
? ? /**
? ? * 手勢狀態(tài):自由狀態(tài)
? ? *
? ? * @see #getPinchMode()
*/
? ? public static final int PINCH_MODE_FREE =0;
? ? /**
? ? * 手勢狀態(tài):單指滾動狀態(tài)
? ? *
? ? * @see #getPinchMode()
*/
? ? public static final int PINCH_MODE_SCROLL =1;
? ? /**
? ? * 手勢狀態(tài):雙指縮放狀態(tài)
? ? *
? ? * @see #getPinchMode()
*/
? ? public static final int PINCH_MODE_SCALE =2;
? ? /**
? ? * 外層變換矩陣,如果是單位矩陣檐束,那么圖片是fit center狀態(tài)
? ? *
? ? * @see #getOuterMatrix(Matrix)
? ? * @see #outerMatrixTo(Matrix, long)
*/
? ? private MatrixmOuterMatrix =new Matrix();
? ? /**
? ? * 矩形遮罩
? ? *
? ? * @see #getMask()
? ? * @see #zoomMaskTo(RectF, long)
*/
? ? private RectFmMask;
? ? /**
? ? * 當前手勢狀態(tài)
? ? *
? ? * @see #getPinchMode()
? ? * @see #PINCH_MODE_FREE
? ? * @see #PINCH_MODE_SCROLL
? ? * @see #PINCH_MODE_SCALE
*/
? ? private int mPinchMode =PINCH_MODE_FREE;
? ? /**
? ? * 獲取外部變換矩陣.
*
? ? * 外部變換矩陣記錄了圖片手勢操作的最終結果,是相對于圖片fit center狀態(tài)的變換.
? ? * 默認值為單位矩陣,此時圖片為fit center狀態(tài).
*
? ? * @param matrix 用于填充結果的對象
? ? * @return 如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
? ? */
? ? public MatrixgetOuterMatrix(Matrix matrix) {
if (matrix ==null) {
matrix =new Matrix(mOuterMatrix);
? ? ? ? }else {
matrix.set(mOuterMatrix);
? ? ? ? }
return matrix;
? ? }
/**
? ? * 獲取內部變換矩陣.
*
? ? * 內部變換矩陣是原圖到fit center狀態(tài)的變換,當原圖尺寸變化或者控件大小變化都會發(fā)生改變
? ? * 當尚未布局或者原圖不存在時,其值無意義.所以在調用前需要確保前置條件有效,否則將影響計算結果.
*
? ? * @param matrix 用于填充結果的對象
? ? * @return 如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
? ? */
? ? public MatrixgetInnerMatrix(Matrix matrix) {
if (matrix ==null) {
matrix =new Matrix();
? ? ? ? }else {
matrix.reset();
? ? ? ? }
if (isReady()) {
//原圖大小
? ? ? ? ? ? RectF tempSrc = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
? ? ? ? ? ? //控件大小
? ? ? ? ? ? RectF tempDst = MathUtils.rectFTake(0, 0, getWidth(), getHeight());
? ? ? ? ? ? //計算fit center矩陣
? ? ? ? ? ? matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);
? ? ? ? ? ? //釋放臨時對象
? ? ? ? ? ? MathUtils.rectFGiven(tempDst);
? ? ? ? ? ? MathUtils.rectFGiven(tempSrc);
? ? ? ? }
return matrix;
? ? }
/**
? ? * 獲取圖片總變換矩陣.
*
? ? * 總變換矩陣為內部變換矩陣x外部變換矩陣,決定了原圖到所見最終狀態(tài)的變換
? ? * 當尚未布局或者原圖不存在時,其值無意義.所以在調用前需要確保前置條件有效,否則將影響計算結果.
*
? ? * @param matrix 用于填充結果的對象
? ? * @return 如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
? ? *
? ? * @see #getOuterMatrix(Matrix)
? ? * @see #getInnerMatrix(Matrix)
*/
? ? public MatrixgetCurrentImageMatrix(Matrix matrix) {
//獲取內部變換矩陣
? ? ? ? matrix = getInnerMatrix(matrix);
? ? ? ? //乘上外部變換矩陣
? ? ? ? matrix.postConcat(mOuterMatrix);
? ? ? ? return matrix;
? ? }
/**
? ? * 獲取當前變換后的圖片位置和尺寸
? ? *
? ? * 當尚未布局或者原圖不存在時,其值無意義.所以在調用前需要確保前置條件有效,否則將影響計算結果.
*
? ? * @param rectF 用于填充結果的對象
? ? * @return 如果傳了rectF參數(shù)則將rectF填充后返回,否則new一個填充返回
? ? *
? ? * @see #getCurrentImageMatrix(Matrix)
*/
? ? public RectFgetImageBound(RectF rectF) {
if (rectF ==null) {
rectF =new RectF();
? ? ? ? }else {
rectF.setEmpty();
? ? ? ? }
if (!isReady()) {
return rectF;
? ? ? ? }else {
//申請一個空matrix
? ? ? ? ? ? Matrix matrix = MathUtils.matrixTake();
? ? ? ? ? ? //獲取當前總變換矩陣
? ? ? ? ? ? getCurrentImageMatrix(matrix);
? ? ? ? ? ? //對原圖矩形進行變換得到當前顯示矩形
? ? ? ? ? ? rectF.set(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
? ? ? ? ? ? matrix.mapRect(rectF);
? ? ? ? ? ? //釋放臨時matrix
? ? ? ? ? ? MathUtils.matrixGiven(matrix);
? ? ? ? ? ? return rectF;
? ? ? ? }
}
/**
? ? * 獲取當前設置的mask
*
? ? * @return 返回當前的mask對象副本,如果當前沒有設置mask則返回null
*/
? ? public RectFgetMask() {
if (mMask !=null) {
return new RectF(mMask);
? ? ? ? }else {
return null;
? ? ? ? }
}
/**
? ? * 獲取當前手勢狀態(tài)
? ? *
? ? * @see #PINCH_MODE_FREE
? ? * @see #PINCH_MODE_SCROLL
? ? * @see #PINCH_MODE_SCALE
*/
? ? public int getPinchMode() {
return mPinchMode;
? ? }
/**
? ? * 與ViewPager結合的時候使用
? ? * @param direction
? ? * @return
? ? */
? ? @Override
? ? public boolean canScrollHorizontally(int direction) {
if (mPinchMode == PinchImageView.PINCH_MODE_SCALE) {
return true;
? ? ? ? }
RectF bound = getImageBound(null);
? ? ? ? if (bound ==null) {
return false;
? ? ? ? }
if (bound.isEmpty()) {
return false;
? ? ? ? }
if (direction >0) {
return bound.right > getWidth();
? ? ? ? }else {
return bound.left <0;
? ? ? ? }
}
/**
? ? * 與ViewPager結合的時候使用
? ? * @param direction
? ? * @return
? ? */
? ? @Override
? ? public boolean canScrollVertically(int direction) {
if (mPinchMode == PinchImageView.PINCH_MODE_SCALE) {
return true;
? ? ? ? }
RectF bound = getImageBound(null);
? ? ? ? if (bound ==null) {
return false;
? ? ? ? }
if (bound.isEmpty()) {
return false;
? ? ? ? }
if (direction >0) {
return bound.bottom > getHeight();
? ? ? ? }else {
return bound.top <0;
? ? ? ? }
}
////////////////////////////////公共狀態(tài)設置////////////////////////////////
? ? /**
? ? * 執(zhí)行當前outerMatrix到指定outerMatrix漸變的動畫
? ? *
? ? * 調用此方法會停止正在進行中的手勢以及手勢動畫.
? ? * 當duration為0時,outerMatrix值會被立即設置而不會啟動動畫.
*
? ? * @param endMatrix 動畫目標矩陣
? ? * @param duration 動畫持續(xù)時間
? ? *
? ? * @see #getOuterMatrix(Matrix)
*/
? ? public void outerMatrixTo(Matrix endMatrix, long duration) {
if (endMatrix ==null) {
return;
? ? ? ? }
//將手勢設置為PINCH_MODE_FREE將停止后續(xù)手勢的觸發(fā)
? ? ? ? mPinchMode =PINCH_MODE_FREE;
? ? ? ? //停止所有正在進行的動畫
? ? ? ? cancelAllAnimator();
? ? ? ? //如果時間不合法立即執(zhí)行結果
? ? ? ? if (duration <=0) {
mOuterMatrix.set(endMatrix);
? ? ? ? ? ? dispatchOuterMatrixChanged();
? ? ? ? ? ? invalidate();
? ? ? ? }else {
//創(chuàng)建矩陣變化動畫
? ? ? ? ? ? mScaleAnimator =new ScaleAnimator(mOuterMatrix, endMatrix, duration);
? ? ? ? ? ? mScaleAnimator.start();
? ? ? ? }
}
/**
? ? * 執(zhí)行當前mask到指定mask的變化動畫
? ? *
? ? * 調用此方法不會停止手勢以及手勢相關動畫,但會停止正在進行的mask動畫.
? ? * 當前mask為null時,則不執(zhí)行動畫立即設置為目標mask.
? ? * 當duration為0時,立即將當前mask設置為目標mask,不會執(zhí)行動畫.
*
? ? * @param mask 動畫目標mask
? ? * @param duration 動畫持續(xù)時間
? ? *
? ? * @see #getMask()
*/
? ? public void zoomMaskTo(RectF mask, long duration) {
if (mask ==null) {
return;
? ? ? ? }
//停止mask動畫
? ? ? ? if (mMaskAnimator !=null) {
mMaskAnimator.cancel();
? ? ? ? ? ? mMaskAnimator =null;
? ? ? ? }
//如果duration為0或者之前沒有設置過mask,不執(zhí)行動畫,立即設置
? ? ? ? if (duration <=0 ||mMask ==null) {
if (mMask ==null) {
mMask =new RectF();
? ? ? ? ? ? }
mMask.set(mask);
? ? ? ? ? ? invalidate();
? ? ? ? }else {
//執(zhí)行mask動畫
? ? ? ? ? ? mMaskAnimator =new MaskAnimator(mMask, mask, duration);
? ? ? ? ? ? mMaskAnimator.start();
? ? ? ? }
}
/**
? ? * 重置所有狀態(tài)
? ? *
? ? * 重置位置到fit center狀態(tài),清空mask,停止所有手勢,停止所有動畫.
? ? * 但不清空drawable,以及事件綁定相關數(shù)據(jù).
*/
? ? public void reset() {
//重置位置到fit
? ? ? ? mOuterMatrix.reset();
? ? ? ? dispatchOuterMatrixChanged();
? ? ? ? //清空mask
? ? ? ? mMask =null;
? ? ? ? //停止所有手勢
? ? ? ? mPinchMode =PINCH_MODE_FREE;
? ? ? ? mLastMovePoint.set(0, 0);
? ? ? ? mScaleCenter.set(0, 0);
? ? ? ? mScaleBase =0;
? ? ? ? //停止所有動畫
? ? ? ? if (mMaskAnimator !=null) {
mMaskAnimator.cancel();
? ? ? ? ? ? mMaskAnimator =null;
? ? ? ? }
cancelAllAnimator();
? ? ? ? //重繪
? ? ? ? invalidate();
? ? }
////////////////////////////////對外廣播事件////////////////////////////////
? ? /**
? ? * 外部矩陣變化事件通知監(jiān)聽器
? ? */
? ? public interface OuterMatrixChangedListener {
/**
? ? ? ? * 外部矩陣變化回調
? ? ? ? *
? ? ? ? * 外部矩陣的任何變化后都收到此回調.
? ? ? ? * 外部矩陣變化后,總變化矩陣,圖片的展示位置都將發(fā)生變化.
*
? ? ? ? * @param pinchImageView
? ? ? ? *
? ? ? ? * @see #getOuterMatrix(Matrix)
? ? ? ? * @see #getCurrentImageMatrix(Matrix)
? ? ? ? * @see #getImageBound(RectF)
*/
? ? ? ? void onOuterMatrixChanged(PinchImageView pinchImageView);
? ? }
/**
? ? * 所有OuterMatrixChangedListener監(jiān)聽列表
? ? *
? ? * @see #addOuterMatrixChangedListener(OuterMatrixChangedListener)
? ? * @see #removeOuterMatrixChangedListener(OuterMatrixChangedListener)
*/
? ? private ListmOuterMatrixChangedListeners;
? ? /**
? ? * 當mOuterMatrixChangedListeners被鎖定不允許修改時,臨時將修改寫到這個副本中
? ? *
? ? * @see #mOuterMatrixChangedListeners
*/
? ? private ListmOuterMatrixChangedListenersCopy;
? ? /**
? ? * mOuterMatrixChangedListeners的修改鎖定
? ? *
? ? * 當進入dispatchOuterMatrixChanged方法時,被加1,退出前被減1
*
? ? * @see #dispatchOuterMatrixChanged()
? ? * @see #addOuterMatrixChangedListener(OuterMatrixChangedListener)
? ? * @see #removeOuterMatrixChangedListener(OuterMatrixChangedListener)
*/
? ? private int mDispatchOuterMatrixChangedLock;
? ? /**
? ? * 添加外部矩陣變化監(jiān)聽
? ? *
? ? * @param listener
? ? */
? ? public void addOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if (listener ==null) {
return;
? ? ? ? }
//如果監(jiān)聽列表沒有被修改鎖定直接將監(jiān)聽添加到監(jiān)聽列表
? ? ? ? if (mDispatchOuterMatrixChangedLock ==0) {
if (mOuterMatrixChangedListeners ==null) {
mOuterMatrixChangedListeners =new ArrayList();
? ? ? ? ? ? }
mOuterMatrixChangedListeners.add(listener);
? ? ? ? }else {
//如果監(jiān)聽列表修改被鎖定,那么嘗試在監(jiān)聽列表副本上添加
? ? ? ? ? ? //監(jiān)聽列表副本將會在鎖定被解除時替換到監(jiān)聽列表里
? ? ? ? ? ? if (mOuterMatrixChangedListenersCopy ==null) {
if (mOuterMatrixChangedListeners !=null) {
mOuterMatrixChangedListenersCopy =new ArrayList(mOuterMatrixChangedListeners);
? ? ? ? ? ? ? ? }else {
mOuterMatrixChangedListenersCopy =new ArrayList();
? ? ? ? ? ? ? ? }
}
mOuterMatrixChangedListenersCopy.add(listener);
? ? ? ? }
}
/**
? ? * 刪除外部矩陣變化監(jiān)聽
? ? *
? ? * @param listener
? ? */
? ? public void removeOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if (listener ==null) {
return;
? ? ? ? }
//如果監(jiān)聽列表沒有被修改鎖定直接在監(jiān)聽列表數(shù)據(jù)結構上修改
? ? ? ? if (mDispatchOuterMatrixChangedLock ==0) {
if (mOuterMatrixChangedListeners !=null) {
mOuterMatrixChangedListeners.remove(listener);
? ? ? ? ? ? }
}else {
//如果監(jiān)聽列表被修改鎖定,那么就在其副本上修改
? ? ? ? ? ? //其副本將會在鎖定解除時替換回監(jiān)聽列表
? ? ? ? ? ? if (mOuterMatrixChangedListenersCopy ==null) {
if (mOuterMatrixChangedListeners !=null) {
mOuterMatrixChangedListenersCopy =new ArrayList(mOuterMatrixChangedListeners);
? ? ? ? ? ? ? ? }
}
if (mOuterMatrixChangedListenersCopy !=null) {
mOuterMatrixChangedListenersCopy.remove(listener);
? ? ? ? ? ? }
}
}
/**
? ? * 觸發(fā)外部矩陣修改事件
? ? *
? ? * 需要在每次給外部矩陣設置值時都調用此方法.
*
? ? * @see #mOuterMatrix
*/
? ? private void dispatchOuterMatrixChanged() {
if (mOuterMatrixChangedListeners ==null) {
return;
? ? ? ? }
//增加鎖
? ? ? ? //這里之所以用計數(shù)器做鎖定是因為可能在鎖定期間又間接調用了此方法產生遞歸
? ? ? ? //使用boolean無法判斷遞歸結束
? ? ? ? mDispatchOuterMatrixChangedLock++;
? ? ? ? //在列表循環(huán)過程中不允許修改列表,否則將引發(fā)崩潰
? ? ? ? for (OuterMatrixChangedListener listener :mOuterMatrixChangedListeners) {
listener.onOuterMatrixChanged(this);
? ? ? ? }
//減鎖
? ? ? ? mDispatchOuterMatrixChangedLock--;
? ? ? ? //如果是遞歸的情況,mDispatchOuterMatrixChangedLock可能大于1,只有減到0才能算列表的鎖定解除
? ? ? ? if (mDispatchOuterMatrixChangedLock ==0) {
//如果期間有修改列表,那么副本將不為null
? ? ? ? ? ? if (mOuterMatrixChangedListenersCopy !=null) {
//將副本替換掉正式的列表
? ? ? ? ? ? ? ? mOuterMatrixChangedListeners =mOuterMatrixChangedListenersCopy;
? ? ? ? ? ? ? ? //清空副本
? ? ? ? ? ? ? ? mOuterMatrixChangedListenersCopy =null;
? ? ? ? ? ? }
}
}
////////////////////////////////用于重載定制////////////////////////////////
? ? /**
? ? * 獲取圖片最大可放大的比例
? ? *
? ? * 如果放大大于這個比例則不被允許.
? ? * 在雙手縮放過程中如果圖片放大比例大于這個值,手指釋放將回彈到這個比例.
? ? * 在雙擊放大過程中不允許放大比例大于這個值.
? ? * 覆蓋此方法可以定制不同情況使用不同的最大可放大比例.
*
? ? * @return 縮放比例
? ? *
? ? * @see #scaleEnd()
? ? * @see #doubleTap(float, float)
*/
? ? protected float getMaxScale() {
return MAX_SCALE;
? ? }
/**
? ? * 計算雙擊之后圖片接下來應該被縮放的比例
? ? *
? ? * 如果值大于getMaxScale或者小于fit center尺寸辫秧,則實際使用取邊界值.
? ? * 通過覆蓋此方法可以定制不同的圖片被雙擊時使用不同的放大策略.
*
? ? * @param innerScale 當前內部矩陣的縮放值
? ? * @param outerScale 當前外部矩陣的縮放值
? ? * @return 接下來的縮放比例
? ? *
? ? * @see #doubleTap(float, float)
? ? * @see #getMaxScale()
*/
? ? protected float calculateNextScale(float innerScale, float outerScale) {
float currentScale = innerScale * outerScale;
? ? ? ? if (currentScale
return MAX_SCALE;
? ? ? ? }else {
return innerScale;
? ? ? ? }
}
////////////////////////////////初始化////////////////////////////////
? ? public PinchImageView(Context context) {
super(context);
? ? ? ? initView();
? ? }
public PinchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
? ? ? ? initView();
? ? }
public PinchImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
? ? ? ? initView();
? ? }
private void initView() {
//強制設置圖片scaleType為matrix
? ? ? ? super.setScaleType(ScaleType.MATRIX);
? ? }
//不允許設置scaleType,只能用內部設置的matrix
? ? @Override
? ? public void setScaleType(ScaleType scaleType) {}
////////////////////////////////繪制////////////////////////////////
? ? @Override
? ? protected void onDraw(Canvas canvas) {
//在繪制前設置變換矩陣
? ? ? ? if (isReady()) {
Matrix matrix = MathUtils.matrixTake();
? ? ? ? ? ? setImageMatrix(getCurrentImageMatrix(matrix));
? ? ? ? ? ? MathUtils.matrixGiven(matrix);
? ? ? ? }
//對圖像做遮罩處理
? ? ? ? if (mMask !=null) {
canvas.save();
? ? ? ? ? ? canvas.clipRect(mMask);
? ? ? ? ? ? super.onDraw(canvas);
? ? ? ? ? ? canvas.restore();
? ? ? ? }else {
super.onDraw(canvas);
? ? ? ? }
}
////////////////////////////////有效性判斷////////////////////////////////
? ? /**
? ? * 判斷當前情況是否能執(zhí)行手勢相關計算
? ? *
? ? * 包括:是否有圖片,圖片是否有尺寸,控件是否有尺寸.
*
? ? * @return 是否能執(zhí)行手勢相關計算
? ? */
? ? private boolean isReady() {
return getDrawable() !=null && getDrawable().getIntrinsicWidth() >0 && getDrawable().getIntrinsicHeight() >0
? ? ? ? ? ? ? ? && getWidth() >0 && getHeight() >0;
? ? }
////////////////////////////////mask動畫處理////////////////////////////////
? ? /**
? ? * mask修改的動畫
? ? *
? ? * 和圖片的動畫相互獨立.
*
? ? * @see #zoomMaskTo(RectF, long)
*/
? ? private MaskAnimatormMaskAnimator;
? ? /**
? ? * mask變換動畫
? ? *
? ? * 將mask從一個rect動畫到另外一個rect
*/
? ? private class MaskAnimatorextends ValueAnimatorimplements ValueAnimator.AnimatorUpdateListener {
/**
? ? ? ? * 開始mask
*/
? ? ? ? private float[]mStart =new float[4];
? ? ? ? /**
? ? ? ? * 結束mask
*/
? ? ? ? private float[]mEnd =new float[4];
? ? ? ? /**
? ? ? ? * 中間結果mask
*/
? ? ? ? private float[]mResult =new float[4];
? ? ? ? /**
? ? ? ? * 創(chuàng)建mask變換動畫
? ? ? ? *
? ? ? ? * @param start 動畫起始狀態(tài)
? ? ? ? * @param end 動畫終點狀態(tài)
? ? ? ? * @param duration 動畫持續(xù)時間
? ? ? ? */
? ? ? ? public MaskAnimator(RectF start, RectF end, long duration) {
super();
? ? ? ? ? ? setFloatValues(0, 1f);
? ? ? ? ? ? setDuration(duration);
? ? ? ? ? ? addUpdateListener(this);
? ? ? ? ? ? //將起點終點拷貝到數(shù)組方便計算
? ? ? ? ? ? mStart[0] = start.left;
? ? ? ? ? ? mStart[1] = start.top;
? ? ? ? ? ? mStart[2] = start.right;
? ? ? ? ? ? mStart[3] = start.bottom;
? ? ? ? ? ? mEnd[0] = end.left;
? ? ? ? ? ? mEnd[1] = end.top;
? ? ? ? ? ? mEnd[2] = end.right;
? ? ? ? ? ? mEnd[3] = end.bottom;
? ? ? ? }
@Override
? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
//獲取動畫進度,0-1范圍
? ? ? ? ? ? float value = (Float) animation.getAnimatedValue();
? ? ? ? ? ? //根據(jù)進度對起點終點之間做插值
? ? ? ? ? ? for (int i =0; i <4; i++) {
mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;
? ? ? ? ? ? }
//期間mask有可能被置空了,所以判斷一下
? ? ? ? ? ? if (mMask ==null) {
mMask =new RectF();
? ? ? ? ? ? }
//設置新的mask并繪制
? ? ? ? ? ? mMask.set(mResult[0], mResult[1], mResult[2], mResult[3]);
? ? ? ? ? ? invalidate();
? ? ? ? }
}
////////////////////////////////手勢動畫處理////////////////////////////////
? ? /**
? ? * 在單指模式下:
? ? * 記錄上一次手指的位置,用于計算新的位置和上一次位置的差值.
*
? ? * 雙指模式下:
? ? * 記錄兩個手指的中點,作為和mScaleCenter綁定的點.
? ? * 這個綁定可以保證mScaleCenter無論如何都會跟隨這個中點.
*
? ? * @see #mScaleCenter
? ? * @see #scale(PointF, float, float, PointF)
? ? * @see #scaleEnd()
*/
? ? private PointFmLastMovePoint =new PointF();
? ? /**
? ? * 縮放模式下圖片的縮放中點.
*
? ? * 為其指代的點經(jīng)過innerMatrix變換之后的值.
? ? * 其指代的點在手勢過程中始終跟隨mLastMovePoint.
? ? * 通過雙指縮放時,其為縮放中心點.
*
? ? * @see #saveScaleContext(float, float, float, float)
? ? * @see #mLastMovePoint
? ? * @see #scale(PointF, float, float, PointF)
*/
? ? private PointFmScaleCenter =new PointF();
? ? /**
? ? * 縮放模式下的基礎縮放比例
? ? *
? ? * 為外層縮放值除以開始縮放時兩指距離.
? ? * 其值乘上最新的兩指之間距離為最新的圖片縮放比例.
*
? ? * @see #saveScaleContext(float, float, float, float)
? ? * @see #scale(PointF, float, float, PointF)
*/
? ? private float mScaleBase =0;
? ? /**
? ? * 圖片縮放動畫
? ? *
? ? * 縮放模式把圖片的位置大小超出限制之后觸發(fā).
? ? * 雙擊圖片放大或縮小時觸發(fā).
? ? * 手動調用outerMatrixTo觸發(fā).
*
? ? * @see #scaleEnd()
? ? * @see #doubleTap(float, float)
? ? * @see #outerMatrixTo(Matrix, long)
*/
? ? private ScaleAnimatormScaleAnimator;
? ? /**
? ? * 滑動產生的慣性動畫
? ? *
? ? * @see #fling(float, float)
*/
? ? private FlingAnimatormFlingAnimator;
? ? /**
? ? * 常用手勢處理
? ? *
? ? * 在onTouchEvent末尾被執(zhí)行.
*/
? ? private GestureDetectormGestureDetector =new GestureDetector(PinchImageView.this.getContext(), new GestureDetector.SimpleOnGestureListener() {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//只有在單指模式結束之后才允許執(zhí)行fling
? ? ? ? ? ? if (mPinchMode ==PINCH_MODE_FREE && !(mScaleAnimator !=null &&mScaleAnimator.isRunning())) {
fling(velocityX, velocityY);
? ? ? ? ? ? }
return true;
? ? ? ? }
public void onLongPress(MotionEvent e) {
//觸發(fā)長按
? ? ? ? ? ? if (mOnLongClickListener !=null) {
mOnLongClickListener.onLongClick(PinchImageView.this);
? ? ? ? ? ? }
}
public boolean onDoubleTap(MotionEvent e) {
//當手指快速第二次按下觸發(fā),此時必須是單指模式才允許執(zhí)行doubleTap
? ? ? ? ? ? if (mPinchMode ==PINCH_MODE_SCROLL && !(mScaleAnimator !=null &&mScaleAnimator.isRunning())) {
doubleTap(e.getX(), e.getY());
? ? ? ? ? ? }
return true;
? ? ? ? }
public boolean onSingleTapConfirmed(MotionEvent e) {
//觸發(fā)點擊
? ? ? ? ? ? if (mOnClickListener !=null) {
mOnClickListener.onClick(PinchImageView.this);
? ? ? ? ? ? }
return true;
? ? ? ? }
});
? ? @Override
? ? public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
? ? ? ? int action = event.getAction() & MotionEvent.ACTION_MASK;
? ? ? ? //最后一個點抬起或者取消被丧,結束所有模式
? ? ? ? if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
//如果之前是縮放模式,還需要觸發(fā)一下縮放結束動畫
? ? ? ? ? ? if (mPinchMode ==PINCH_MODE_SCALE) {
scaleEnd();
? ? ? ? ? ? }
mPinchMode =PINCH_MODE_FREE;
? ? ? ? }else if (action == MotionEvent.ACTION_POINTER_UP) {
//多個手指情況下抬起一個手指,此時需要是縮放模式才觸發(fā)
? ? ? ? ? ? if (mPinchMode ==PINCH_MODE_SCALE) {
//抬起的點如果大于2盟戏,那么縮放模式還有效绪妹,但是有可能初始點變了,重新測量初始點
? ? ? ? ? ? ? ? if (event.getPointerCount() >2) {
//如果還沒結束縮放模式柿究,但是第一個點抬起了邮旷,那么讓第二個點和第三個點作為縮放控制點
? ? ? ? ? ? ? ? ? ? if (event.getAction() >>8 ==0) {
saveScaleContext(event.getX(1), event.getY(1), event.getX(2), event.getY(2));
? ? ? ? ? ? ? ? ? ? ? ? //如果還沒結束縮放模式,但是第二個點抬起了蝇摸,那么讓第一個點和第三個點作為縮放控制點
? ? ? ? ? ? ? ? ? ? }else if (event.getAction() >>8 ==1) {
saveScaleContext(event.getX(0), event.getY(0), event.getX(2), event.getY(2));
? ? ? ? ? ? ? ? ? ? }
}
//如果抬起的點等于2,那么此時只剩下一個點,也不允許進入單指模式,因為此時可能圖片沒有在正確的位置上
? ? ? ? ? ? }
//第一個點按下婶肩,開啟滾動模式,記錄開始滾動的點
? ? ? ? }else if (action == MotionEvent.ACTION_DOWN) {
//在矩陣動畫過程中不允許啟動滾動模式
? ? ? ? ? ? if (!(mScaleAnimator !=null &&mScaleAnimator.isRunning())) {
//停止所有動畫
? ? ? ? ? ? ? ? cancelAllAnimator();
? ? ? ? ? ? ? ? //切換到滾動模式
? ? ? ? ? ? ? ? mPinchMode =PINCH_MODE_SCROLL;
? ? ? ? ? ? ? ? //保存觸發(fā)點用于move計算差值
? ? ? ? ? ? ? ? mLastMovePoint.set(event.getX(), event.getY());
? ? ? ? ? ? }
//非第一個點按下貌夕,關閉滾動模式律歼,開啟縮放模式,記錄縮放模式的一些初始數(shù)據(jù)
? ? ? ? }else if (action == MotionEvent.ACTION_POINTER_DOWN) {
//停止所有動畫
? ? ? ? ? ? cancelAllAnimator();
? ? ? ? ? ? //切換到縮放模式
? ? ? ? ? ? mPinchMode =PINCH_MODE_SCALE;
? ? ? ? ? ? //保存縮放的兩個手指
? ? ? ? ? ? saveScaleContext(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
? ? ? ? }else if (action == MotionEvent.ACTION_MOVE) {
if (!(mScaleAnimator !=null &&mScaleAnimator.isRunning())) {
//在滾動模式下移動
? ? ? ? ? ? ? ? if (mPinchMode ==PINCH_MODE_SCROLL) {
//每次移動產生一個差值累積到圖片位置上
? ? ? ? ? ? ? ? ? ? scrollBy(event.getX() -mLastMovePoint.x, event.getY() -mLastMovePoint.y);
? ? ? ? ? ? ? ? ? ? //記錄新的移動點
? ? ? ? ? ? ? ? ? ? mLastMovePoint.set(event.getX(), event.getY());
? ? ? ? ? ? ? ? ? ? //在縮放模式下移動
? ? ? ? ? ? ? ? }else if (mPinchMode ==PINCH_MODE_SCALE && event.getPointerCount() >1) {
//兩個縮放點間的距離
? ? ? ? ? ? ? ? ? ? float distance = MathUtils.getDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
? ? ? ? ? ? ? ? ? ? //保存縮放點中點
? ? ? ? ? ? ? ? ? ? float[] lineCenter = MathUtils.getCenterPoint(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
? ? ? ? ? ? ? ? ? ? mLastMovePoint.set(lineCenter[0], lineCenter[1]);
? ? ? ? ? ? ? ? ? ? //處理縮放
? ? ? ? ? ? ? ? ? ? scale(mScaleCenter, mScaleBase, distance, mLastMovePoint);
? ? ? ? ? ? ? ? }
}
}
//無論如何都處理各種外部手勢
? ? ? ? mGestureDetector.onTouchEvent(event);
return true;
? ? }
/**
? ? * 讓圖片移動一段距離
? ? *
? ? * 不能移動超過可移動范圍,超過了就到可移動范圍邊界為止.
*
? ? * @param xDiff 移動距離
? ? * @param yDiff 移動距離
? ? * @return 是否改變了位置
? ? */
? ? private boolean scrollBy(float xDiff, float yDiff) {
if (!isReady()) {
return false;
? ? ? ? }
//原圖方框
? ? ? ? RectF bound = MathUtils.rectFTake();
? ? ? ? getImageBound(bound);
? ? ? ? //控件大小
? ? ? ? float displayWidth = getWidth();
? ? ? ? float displayHeight = getHeight();
? ? ? ? //如果當前圖片寬度小于控件寬度啡专,則不能移動
? ? ? ? if (bound.right - bound.left < displayWidth) {
xDiff =0;
? ? ? ? ? ? //如果圖片左邊在移動后超出控件左邊
? ? ? ? }else if (bound.left + xDiff >0) {
//如果在移動之前是沒超出的苗膝,計算應該移動的距離
? ? ? ? ? ? if (bound.left <0) {
xDiff = -bound.left;
? ? ? ? ? ? ? ? //否則無法移動
? ? ? ? ? ? }else {
xDiff =0;
? ? ? ? ? ? }
//如果圖片右邊在移動后超出控件右邊
? ? ? ? }else if (bound.right + xDiff < displayWidth) {
//如果在移動之前是沒超出的,計算應該移動的距離
? ? ? ? ? ? if (bound.right > displayWidth) {
xDiff = displayWidth - bound.right;
? ? ? ? ? ? ? ? //否則無法移動
? ? ? ? ? ? }else {
xDiff =0;
? ? ? ? ? ? }
}
//以下同理
? ? ? ? if (bound.bottom - bound.top < displayHeight) {
yDiff =0;
? ? ? ? }else if (bound.top + yDiff >0) {
if (bound.top <0) {
yDiff = -bound.top;
? ? ? ? ? ? }else {
yDiff =0;
? ? ? ? ? ? }
}else if (bound.bottom + yDiff < displayHeight) {
if (bound.bottom > displayHeight) {
yDiff = displayHeight - bound.bottom;
? ? ? ? ? ? }else {
yDiff =0;
? ? ? ? ? ? }
}
MathUtils.rectFGiven(bound);
? ? ? ? //應用移動變換
? ? ? ? mOuterMatrix.postTranslate(xDiff, yDiff);
? ? ? ? dispatchOuterMatrixChanged();
? ? ? ? //觸發(fā)重繪
? ? ? ? invalidate();
? ? ? ? //檢查是否有變化
? ? ? ? if (xDiff !=0 || yDiff !=0) {
return true;
? ? ? ? }else {
return false;
? ? ? ? }
}
/**
? ? * 記錄縮放前的一些信息
? ? *
? ? * 保存基礎縮放值.
? ? * 保存圖片縮放中點.
*
? ? * @param x1 縮放第一個手指
? ? * @param y1 縮放第一個手指
? ? * @param x2 縮放第二個手指
? ? * @param y2 縮放第二個手指
? ? */
? ? private void saveScaleContext(float x1, float y1, float x2, float y2) {
//記錄基礎縮放值,其中圖片縮放比例按照x方向來計算
? ? ? ? //理論上圖片應該是等比的,x和y方向比例相同
? ? ? ? //但是有可能外部設定了不規(guī)范的值.
? ? ? ? //但是后續(xù)的scale操作會將xy不等的縮放值糾正,改成和x方向相同
? ? ? ? mScaleBase = MathUtils.getMatrixScale(mOuterMatrix)[0] / MathUtils.getDistance(x1, y1, x2, y2);
? ? ? ? //兩手指的中點在屏幕上落在了圖片的某個點上,圖片上的這個點在經(jīng)過總矩陣變換后和手指中點相同
? ? ? ? //現(xiàn)在我們需要得到圖片上這個點在圖片是fit center狀態(tài)下在屏幕上的位置
? ? ? ? //因為后續(xù)的計算都是基于圖片是fit center狀態(tài)下進行變換
? ? ? ? //所以需要把兩手指中點除以外層變換矩陣得到mScaleCenter
? ? ? ? float[] center = MathUtils.inverseMatrixPoint(MathUtils.getCenterPoint(x1, y1, x2, y2), mOuterMatrix);
? ? ? ? mScaleCenter.set(center[0], center[1]);
? ? }
/**
? ? * 對圖片按照一些手勢信息進行縮放
? ? *
? ? * @param scaleCenter mScaleCenter
? ? * @param scaleBase mScaleBase
? ? * @param distance 手指兩點之間距離
? ? * @param lineCenter 手指兩點之間中點
? ? *
? ? * @see #mScaleCenter
? ? * @see #mScaleBase
*/
? ? private void scale(PointF scaleCenter, float scaleBase, float distance, PointF lineCenter) {
if (!isReady()) {
return;
? ? ? ? }
//計算圖片從fit center狀態(tài)到目標狀態(tài)的縮放比例
? ? ? ? float scale = scaleBase * distance;
? ? ? ? Matrix matrix = MathUtils.matrixTake();
? ? ? ? //按照圖片縮放中心縮放植旧,并且讓縮放中心在縮放點中點上
? ? ? ? matrix.postScale(scale, scale, scaleCenter.x, scaleCenter.y);
? ? ? ? //讓圖片的縮放中點跟隨手指縮放中點
? ? ? ? matrix.postTranslate(lineCenter.x - scaleCenter.x, lineCenter.y - scaleCenter.y);
? ? ? ? //應用變換
? ? ? ? mOuterMatrix.set(matrix);
? ? ? ? MathUtils.matrixGiven(matrix);
? ? ? ? dispatchOuterMatrixChanged();
? ? ? ? //重繪
? ? ? ? invalidate();
? ? }
/**
? ? * 雙擊后放大或者縮小
? ? *
? ? * 將圖片縮放比例縮放到nextScale指定的值.
? ? * 但nextScale值不能大于最大縮放值不能小于fit center情況下的縮放值.
? ? * 將雙擊的點盡量移動到控件中心.
*
? ? * @param x 雙擊的點
? ? * @param y 雙擊的點
? ? *
? ? * @see #calculateNextScale(float, float)
? ? * @see #getMaxScale()
*/
? ? private void doubleTap(float x, float y) {
if (!isReady()) {
return;
? ? ? ? }
//獲取第一層變換矩陣
? ? ? ? Matrix innerMatrix = MathUtils.matrixTake();
? ? ? ? getInnerMatrix(innerMatrix);
? ? ? ? //當前總的縮放比例
? ? ? ? float innerScale = MathUtils.getMatrixScale(innerMatrix)[0];
? ? ? ? float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
? ? ? ? float currentScale = innerScale * outerScale;
? ? ? ? //控件大小
? ? ? ? float displayWidth = getWidth();
? ? ? ? float displayHeight = getHeight();
? ? ? ? //最大放大大小
? ? ? ? float maxScale = getMaxScale();
? ? ? ? //接下來要放大的大小
? ? ? ? float nextScale = calculateNextScale(innerScale, outerScale);
? ? ? ? //如果接下來放大大于最大值或者小于fit center值辱揭,則取邊界
? ? ? ? if (nextScale > maxScale) {
nextScale = maxScale;
? ? ? ? }
if (nextScale < innerScale) {
nextScale = innerScale;
? ? ? ? }
//開始計算縮放動畫的結果矩陣
? ? ? ? Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);
? ? ? ? //計算還需縮放的倍數(shù)
? ? ? ? animEnd.postScale(nextScale / currentScale, nextScale / currentScale, x, y);
? ? ? ? //將放大點移動到控件中心
? ? ? ? animEnd.postTranslate(displayWidth /2f - x, displayHeight /2f - y);
? ? ? ? //得到放大之后的圖片方框
? ? ? ? Matrix testMatrix = MathUtils.matrixTake(innerMatrix);
? ? ? ? testMatrix.postConcat(animEnd);
? ? ? ? RectF testBound = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
? ? ? ? testMatrix.mapRect(testBound);
? ? ? ? //修正位置
? ? ? ? float postX =0;
? ? ? ? float postY =0;
? ? ? ? if (testBound.right - testBound.left < displayWidth) {
postX = displayWidth /2f - (testBound.right + testBound.left) /2f;
? ? ? ? }else if (testBound.left >0) {
postX = -testBound.left;
? ? ? ? }else if (testBound.right < displayWidth) {
postX = displayWidth - testBound.right;
? ? ? ? }
if (testBound.bottom - testBound.top < displayHeight) {
postY = displayHeight /2f - (testBound.bottom + testBound.top) /2f;
? ? ? ? }else if (testBound.top >0) {
postY = -testBound.top;
? ? ? ? }else if (testBound.bottom < displayHeight) {
postY = displayHeight - testBound.bottom;
? ? ? ? }
//應用修正位置
? ? ? ? animEnd.postTranslate(postX, postY);
? ? ? ? //清理當前可能正在執(zhí)行的動畫
? ? ? ? cancelAllAnimator();
? ? ? ? //啟動矩陣動畫
? ? ? ? mScaleAnimator =new ScaleAnimator(mOuterMatrix, animEnd);
? ? ? ? mScaleAnimator.start();
? ? ? ? //清理臨時變量
? ? ? ? MathUtils.rectFGiven(testBound);
? ? ? ? MathUtils.matrixGiven(testMatrix);
? ? ? ? MathUtils.matrixGiven(animEnd);
? ? ? ? MathUtils.matrixGiven(innerMatrix);
? ? }
/**
? ? * 當縮放操作結束動畫
? ? *
? ? * 如果圖片超過邊界,找到最近的位置動畫恢復.
? ? * 如果圖片縮放尺寸超過最大值或者最小值,找到最近的值動畫恢復.
*/
? ? private void scaleEnd() {
if (!isReady()) {
return;
? ? ? ? }
//是否修正了位置
? ? ? ? boolean change =false;
? ? ? ? //獲取圖片整體的變換矩陣
? ? ? ? Matrix currentMatrix = MathUtils.matrixTake();
? ? ? ? getCurrentImageMatrix(currentMatrix);
? ? ? ? //整體縮放比例
? ? ? ? float currentScale = MathUtils.getMatrixScale(currentMatrix)[0];
? ? ? ? //第二層縮放比例
? ? ? ? float outerScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
? ? ? ? //控件大小
? ? ? ? float displayWidth = getWidth();
? ? ? ? float displayHeight = getHeight();
? ? ? ? //最大縮放比例
? ? ? ? float maxScale = getMaxScale();
? ? ? ? //比例修正
? ? ? ? float scalePost =1f;
? ? ? ? //位置修正
? ? ? ? float postX =0;
? ? ? ? float postY =0;
? ? ? ? //如果整體縮放比例大于最大比例,進行縮放修正
? ? ? ? if (currentScale > maxScale) {
scalePost = maxScale / currentScale;
? ? ? ? }
//如果縮放修正后整體導致第二層縮放小于1(就是圖片比fit center狀態(tài)還胁「健)问窃,重新修正縮放
? ? ? ? if (outerScale * scalePost <1f) {
scalePost =1f / outerScale;
? ? ? ? }
//如果縮放修正不為1,說明進行了修正
? ? ? ? if (scalePost !=1f) {
change =true;
? ? ? ? }
//嘗試根據(jù)縮放點進行縮放修正
? ? ? ? Matrix testMatrix = MathUtils.matrixTake(currentMatrix);
? ? ? ? testMatrix.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
? ? ? ? RectF testBound = MathUtils.rectFTake(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
? ? ? ? //獲取縮放修正后的圖片方框
? ? ? ? testMatrix.mapRect(testBound);
? ? ? ? //檢測縮放修正后位置有無超出完沪,如果超出進行位置修正
? ? ? ? if (testBound.right - testBound.left < displayWidth) {
postX = displayWidth /2f - (testBound.right + testBound.left) /2f;
? ? ? ? }else if (testBound.left >0) {
postX = -testBound.left;
? ? ? ? }else if (testBound.right < displayWidth) {
postX = displayWidth - testBound.right;
? ? ? ? }
if (testBound.bottom - testBound.top < displayHeight) {
postY = displayHeight /2f - (testBound.bottom + testBound.top) /2f;
? ? ? ? }else if (testBound.top >0) {
postY = -testBound.top;
? ? ? ? }else if (testBound.bottom < displayHeight) {
postY = displayHeight - testBound.bottom;
? ? ? ? }
//如果位置修正不為0域庇,說明進行了修正
? ? ? ? if (postX !=0 || postY !=0) {
change =true;
? ? ? ? }
//只有有執(zhí)行修正才執(zhí)行動畫
? ? ? ? if (change) {
//計算結束矩陣
? ? ? ? ? ? Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);
? ? ? ? ? ? animEnd.postScale(scalePost, scalePost, mLastMovePoint.x, mLastMovePoint.y);
? ? ? ? ? ? animEnd.postTranslate(postX, postY);
? ? ? ? ? ? //清理當前可能正在執(zhí)行的動畫
? ? ? ? ? ? cancelAllAnimator();
? ? ? ? ? ? //啟動矩陣動畫
? ? ? ? ? ? mScaleAnimator =new ScaleAnimator(mOuterMatrix, animEnd);
? ? ? ? ? ? mScaleAnimator.start();
? ? ? ? ? ? //清理臨時變量
? ? ? ? ? ? MathUtils.matrixGiven(animEnd);
? ? ? ? }
//清理臨時變量
? ? ? ? MathUtils.rectFGiven(testBound);
? ? ? ? MathUtils.matrixGiven(testMatrix);
? ? ? ? MathUtils.matrixGiven(currentMatrix);
? ? }
/**
? ? * 執(zhí)行慣性動畫
? ? *
? ? * 動畫在遇到不能移動就停止.
? ? * 動畫速度衰減到很小就停止.
*
? ? * 其中參數(shù)速度單位為 像素/秒
? ? *
? ? * @param vx x方向速度
? ? * @param vy y方向速度
? ? */
? ? private void fling(float vx, float vy) {
if (!isReady()) {
return;
? ? ? ? }
//清理當前可能正在執(zhí)行的動畫
? ? ? ? cancelAllAnimator();
? ? ? ? //創(chuàng)建慣性動畫
? ? ? ? //FlingAnimator單位為 像素/幀,一秒60幀
? ? ? ? mFlingAnimator =new FlingAnimator(vx /60f, vy /60f);
? ? ? ? mFlingAnimator.start();
? ? }
/**
? ? * 停止所有手勢動畫
? ? */
? ? private void cancelAllAnimator() {
if (mScaleAnimator !=null) {
mScaleAnimator.cancel();
? ? ? ? ? ? mScaleAnimator =null;
? ? ? ? }
if (mFlingAnimator !=null) {
mFlingAnimator.cancel();
? ? ? ? ? ? mFlingAnimator =null;
? ? ? ? }
}
/**
? ? * 慣性動畫
? ? *
? ? * 速度逐漸衰減,每幀速度衰減為原來的FLING_DAMPING_FACTOR,當速度衰減到小于1時停止.
? ? * 當圖片不能移動時,動畫停止.
*/
? ? private class FlingAnimatorextends ValueAnimatorimplements ValueAnimator.AnimatorUpdateListener {
/**
? ? ? ? * 速度向量
? ? ? ? */
? ? ? ? private float[]mVector;
? ? ? ? /**
? ? ? ? * 創(chuàng)建慣性動畫
? ? ? ? *
? ? ? ? * 參數(shù)單位為 像素/幀
? ? ? ? *
? ? ? ? * @param vectorX 速度向量
? ? ? ? * @param vectorY 速度向量
? ? ? ? */
? ? ? ? public FlingAnimator(float vectorX, float vectorY) {
super();
? ? ? ? ? ? setFloatValues(0, 1f);
? ? ? ? ? ? setDuration(1000000);
? ? ? ? ? ? addUpdateListener(this);
? ? ? ? ? ? mVector =new float[]{vectorX, vectorY};
? ? ? ? }
@Override
? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
//移動圖像并給出結果
? ? ? ? ? ? boolean result = scrollBy(mVector[0], mVector[1]);
? ? ? ? ? ? //衰減速度
? ? ? ? ? ? mVector[0] *=FLING_DAMPING_FACTOR;
? ? ? ? ? ? mVector[1] *=FLING_DAMPING_FACTOR;
? ? ? ? ? ? //速度太小或者不能移動了就結束
? ? ? ? ? ? if (!result || MathUtils.getDistance(0, 0, mVector[0], mVector[1]) <1f) {
animation.cancel();
? ? ? ? ? ? }
}
}
/**
? ? * 縮放動畫
? ? *
? ? * 在給定時間內從一個矩陣的變化逐漸動畫到另一個矩陣的變化
? ? */
? ? private class ScaleAnimatorextends ValueAnimatorimplements ValueAnimator.AnimatorUpdateListener {
/**
? ? ? ? * 開始矩陣
? ? ? ? */
? ? ? ? private float[]mStart =new float[9];
? ? ? ? /**
? ? ? ? * 結束矩陣
? ? ? ? */
? ? ? ? private float[]mEnd =new float[9];
? ? ? ? /**
? ? ? ? * 中間結果矩陣
? ? ? ? */
? ? ? ? private float[]mResult =new float[9];
? ? ? ? /**
? ? ? ? * 構建一個縮放動畫
? ? ? ? *
? ? ? ? * 從一個矩陣變換到另外一個矩陣
? ? ? ? *
? ? ? ? * @param start 開始矩陣
? ? ? ? * @param end 結束矩陣
? ? ? ? */
? ? ? ? public ScaleAnimator(Matrix start, Matrix end) {
this(start, end, SCALE_ANIMATOR_DURATION);
? ? ? ? }
/**
? ? ? ? * 構建一個縮放動畫
? ? ? ? *
? ? ? ? * 從一個矩陣變換到另外一個矩陣
? ? ? ? *
? ? ? ? * @param start 開始矩陣
? ? ? ? * @param end 結束矩陣
? ? ? ? * @param duration 動畫時間
? ? ? ? */
? ? ? ? public ScaleAnimator(Matrix start, Matrix end, long duration) {
super();
? ? ? ? ? ? setFloatValues(0, 1f);
? ? ? ? ? ? setDuration(duration);
? ? ? ? ? ? addUpdateListener(this);
? ? ? ? ? ? start.getValues(mStart);
? ? ? ? ? ? end.getValues(mEnd);
? ? ? ? }
@Override
? ? ? ? public void onAnimationUpdate(ValueAnimator animation) {
//獲取動畫進度
? ? ? ? ? ? float value = (Float) animation.getAnimatedValue();
? ? ? ? ? ? //根據(jù)動畫進度計算矩陣中間插值
? ? ? ? ? ? for (int i =0; i <9; i++) {
mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;
? ? ? ? ? ? }
//設置矩陣并重繪
? ? ? ? ? ? mOuterMatrix.setValues(mResult);
? ? ? ? ? ? dispatchOuterMatrixChanged();
? ? ? ? ? ? invalidate();
? ? ? ? }
}
////////////////////////////////防止內存抖動復用對象////////////////////////////////
? ? /**
? ? * 對象池
? ? *
? ? * 防止頻繁new對象產生內存抖動.
? ? * 由于對象池最大長度限制,如果吞度量超過對象池容量,仍然會發(fā)生抖動.
? ? * 此時需要增大對象池容量,但是會占用更多內存.
*
? ? * @param 對象池容納的對象類型
? ? */
? ? private static abstract class ObjectsPool {
/**
? ? ? ? * 對象池的最大容量
? ? ? ? */
? ? ? ? private int mSize;
? ? ? ? /**
? ? ? ? * 對象池隊列
? ? ? ? */
? ? ? ? private QueuemQueue;
? ? ? ? /**
? ? ? ? * 創(chuàng)建一個對象池
? ? ? ? *
? ? ? ? * @param size 對象池最大容量
? ? ? ? */
? ? ? ? public ObjectsPool(int size) {
mSize = size;
? ? ? ? ? ? mQueue =new LinkedList();
? ? ? ? }
/**
? ? ? ? * 獲取一個空閑的對象
? ? ? ? *
? ? ? ? * 如果對象池為空,則對象池自己會new一個返回.
? ? ? ? * 如果對象池內有對象,則取一個已存在的返回.
? ? ? ? * take出來的對象用完要記得調用given歸還.
? ? ? ? * 如果不歸還,讓然會發(fā)生內存抖動,但不會引起泄漏.
*
? ? ? ? * @return 可用的對象
? ? ? ? *
? ? ? ? * @see #given(Object)
*/
? ? ? ? public T take() {
//如果池內為空就創(chuàng)建一個
? ? ? ? ? ? if (mQueue.size() ==0) {
return newInstance();
? ? ? ? ? ? }else {
//對象池里有就從頂端拿出來一個返回
? ? ? ? ? ? ? ? return resetInstance(mQueue.poll());
? ? ? ? ? ? }
}
/**
? ? ? ? * 歸還對象池內申請的對象
? ? ? ? *
? ? ? ? * 如果歸還的對象數(shù)量超過對象池容量,那么歸還的對象就會被丟棄.
*
? ? ? ? * @param obj 歸還的對象
? ? ? ? *
? ? ? ? * @see #take()
*/
? ? ? ? public void given(T obj) {
//如果對象池還有空位子就歸還對象
? ? ? ? ? ? if (obj !=null &&mQueue.size()
mQueue.offer(obj);
? ? ? ? ? ? }
}
/**
? ? ? ? * 實例化對象
? ? ? ? *
? ? ? ? * @return 創(chuàng)建的對象
? ? ? ? */
? ? ? ? abstract protected T newInstance();
? ? ? ? /**
? ? ? ? * 重置對象
? ? ? ? *
? ? ? ? * 把對象數(shù)據(jù)清空到就像剛創(chuàng)建的一樣.
*
? ? ? ? * @param obj 需要被重置的對象
? ? ? ? * @return 被重置之后的對象
? ? ? ? */
? ? ? ? abstract protected T resetInstance(T obj);
? ? }
/**
? ? * 矩陣對象池
? ? */
? ? private static class MatrixPoolextends ObjectsPool {
public MatrixPool(int size) {
super(size);
? ? ? ? }
@Override
? ? ? ? protected MatrixnewInstance() {
return new Matrix();
? ? ? ? }
@Override
? ? ? ? protected MatrixresetInstance(Matrix obj) {
obj.reset();
? ? ? ? ? ? return obj;
? ? ? ? }
}
/**
? ? * 矩形對象池
? ? */
? ? private static class RectFPoolextends ObjectsPool {
public RectFPool(int size) {
super(size);
? ? ? ? }
@Override
? ? ? ? protected RectFnewInstance() {
return new RectF();
? ? ? ? }
@Override
? ? ? ? protected RectFresetInstance(RectF obj) {
obj.setEmpty();
? ? ? ? ? ? return obj;
? ? ? ? }
}
////////////////////////////////數(shù)學計算工具類////////////////////////////////
? ? /**
? ? * 數(shù)學計算工具類
? ? */
? ? public static class MathUtils {
/**
? ? ? ? * 矩陣對象池
? ? ? ? */
? ? ? ? private static MatrixPoolmMatrixPool =new MatrixPool(16);
? ? ? ? /**
? ? ? ? * 獲取矩陣對象
? ? ? ? */
? ? ? ? public static MatrixmatrixTake() {
return mMatrixPool.take();
? ? ? ? }
/**
? ? ? ? * 獲取某個矩陣的copy
*/
? ? ? ? public static MatrixmatrixTake(Matrix matrix) {
Matrix result =mMatrixPool.take();
? ? ? ? ? ? if (matrix !=null) {
result.set(matrix);
? ? ? ? ? ? }
return result;
? ? ? ? }
/**
? ? ? ? * 歸還矩陣對象
? ? ? ? */
? ? ? ? public static void matrixGiven(Matrix matrix) {
mMatrixPool.given(matrix);
? ? ? ? }
/**
? ? ? ? * 矩形對象池
? ? ? ? */
? ? ? ? private static RectFPoolmRectFPool =new RectFPool(16);
? ? ? ? /**
? ? ? ? * 獲取矩形對象
? ? ? ? */
? ? ? ? public static RectFrectFTake() {
return mRectFPool.take();
? ? ? ? }
/**
? ? ? ? * 按照指定值獲取矩形對象
? ? ? ? */
? ? ? ? public static RectFrectFTake(float left, float top, float right, float bottom) {
RectF result =mRectFPool.take();
? ? ? ? ? ? result.set(left, top, right, bottom);
? ? ? ? ? ? return result;
? ? ? ? }
/**
? ? ? ? * 獲取某個矩形的副本
? ? ? ? */
? ? ? ? public static RectFrectFTake(RectF rectF) {
RectF result =mRectFPool.take();
? ? ? ? ? ? if (rectF !=null) {
result.set(rectF);
? ? ? ? ? ? }
return result;
? ? ? ? }
/**
? ? ? ? * 歸還矩形對象
? ? ? ? */
? ? ? ? public static void rectFGiven(RectF rectF) {
mRectFPool.given(rectF);
? ? ? ? }
/**
? ? ? ? * 獲取兩點之間距離
? ? ? ? *
? ? ? ? * @param x1 點1
? ? ? ? * @param y1 點1
? ? ? ? * @param x2 點2
? ? ? ? * @param y2 點2
? ? ? ? * @return 距離
? ? ? ? */
? ? ? ? public static float getDistance(float x1, float y1, float x2, float y2) {
float x = x1 - x2;
? ? ? ? ? ? float y = y1 - y2;
? ? ? ? ? ? return (float) Math.sqrt(x * x + y * y);
? ? ? ? }
/**
? ? ? ? * 獲取兩點的中點
? ? ? ? *
? ? ? ? * @param x1 點1
? ? ? ? * @param y1 點1
? ? ? ? * @param x2 點2
? ? ? ? * @param y2 點2
? ? ? ? * @return float[]{x, y}
*/
? ? ? ? public static float[]getCenterPoint(float x1, float y1, float x2, float y2) {
return new float[]{(x1 + x2) /2f, (y1 + y2) /2f};
? ? ? ? }
/**
? ? ? ? * 獲取矩陣的縮放值
? ? ? ? *
? ? ? ? * @param matrix 要計算的矩陣
? ? ? ? * @return float[]{scaleX, scaleY}
*/
? ? ? ? public static float[]getMatrixScale(Matrix matrix) {
if (matrix !=null) {
float[] value =new float[9];
? ? ? ? ? ? ? ? matrix.getValues(value);
? ? ? ? ? ? ? ? return new float[]{value[0], value[4]};
? ? ? ? ? ? }else {
return new float[2];
? ? ? ? ? ? }
}
/**
? ? ? ? * 計算點除以矩陣的值
? ? ? ? *
* matrix.mapPoints(unknownPoint) -> point
? ? ? ? * 已知point和matrix,求unknownPoint的值.
*
? ? ? ? * @param point
? ? ? ? * @param matrix
? ? ? ? * @return unknownPoint
*/
? ? ? ? public static float[]inverseMatrixPoint(float[] point, Matrix matrix) {
if (point !=null && matrix !=null) {
float[] dst =new float[2];
? ? ? ? ? ? ? ? //計算matrix的逆矩陣
? ? ? ? ? ? ? ? Matrix inverse =matrixTake();
? ? ? ? ? ? ? ? matrix.invert(inverse);
? ? ? ? ? ? ? ? //用逆矩陣變換point到dst,dst就是結果
? ? ? ? ? ? ? ? inverse.mapPoints(dst, point);
? ? ? ? ? ? ? ? //清除臨時變量
? ? ? ? ? ? ? ? matrixGiven(inverse);
? ? ? ? ? ? ? ? return dst;
? ? ? ? ? ? }else {
return new float[2];
? ? ? ? ? ? }
}
/**
? ? ? ? * 計算兩個矩形之間的變換矩陣
? ? ? ? *
* unknownMatrix.mapRect(to, from)
? ? ? ? * 已知from矩形和to矩形,求unknownMatrix
*
? ? ? ? * @param from
? ? ? ? * @param to
? ? ? ? * @param result unknownMatrix
*/
? ? ? ? public static void calculateRectTranslateMatrix(RectF from, RectF to, Matrix result) {
if (from ==null || to ==null || result ==null) {
return;
? ? ? ? ? ? }
if (from.width() ==0 || from.height() ==0) {
return;
? ? ? ? ? ? }
result.reset();
? ? ? ? ? ? result.postTranslate(-from.left, -from.top);
? ? ? ? ? ? result.postScale(to.width() / from.width(), to.height() / from.height());
? ? ? ? ? ? result.postTranslate(to.left, to.top);
? ? ? ? }
/**
? ? ? ? * 計算圖片在某個ImageView中的顯示矩形
? ? ? ? *
? ? ? ? * @param container ImageView的Rect
? ? ? ? * @param srcWidth 圖片的寬度
? ? ? ? * @param srcHeight 圖片的高度
? ? ? ? * @param scaleType 圖片在ImageView中的ScaleType
? ? ? ? * @param result 圖片應該在ImageView中展示的矩形
? ? ? ? */
? ? ? ? public static void calculateScaledRectInContainer(RectF container, float srcWidth, float srcHeight, ScaleType scaleType, RectF result) {
if (container ==null || result ==null) {
return;
? ? ? ? ? ? }
if (srcWidth ==0 || srcHeight ==0) {
return;
? ? ? ? ? ? }
//默認scaleType為fit center
? ? ? ? ? ? if (scaleType ==null) {
scaleType = ScaleType.FIT_CENTER;
? ? ? ? ? ? }
result.setEmpty();
? ? ? ? ? ? if (ScaleType.FIT_XY.equals(scaleType)) {
result.set(container);
? ? ? ? ? ? }else if (ScaleType.CENTER.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? matrix.setTranslate((container.width() - srcWidth) *0.5f, (container.height() - srcHeight) *0.5f);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else if (ScaleType.CENTER_CROP.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? float scale;
? ? ? ? ? ? ? ? float dx =0;
? ? ? ? ? ? ? ? float dy =0;
? ? ? ? ? ? ? ? if (srcWidth * container.height() > container.width() * srcHeight) {
scale = container.height() / srcHeight;
? ? ? ? ? ? ? ? ? ? dx = (container.width() - srcWidth * scale) *0.5f;
? ? ? ? ? ? ? ? }else {
scale = container.width() / srcWidth;
? ? ? ? ? ? ? ? ? ? dy = (container.height() - srcHeight * scale) *0.5f;
? ? ? ? ? ? ? ? }
matrix.setScale(scale, scale);
? ? ? ? ? ? ? ? matrix.postTranslate(dx, dy);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else if (ScaleType.CENTER_INSIDE.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? float scale;
? ? ? ? ? ? ? ? float dx;
? ? ? ? ? ? ? ? float dy;
? ? ? ? ? ? ? ? if (srcWidth <= container.width() && srcHeight <= container.height()) {
scale =1f;
? ? ? ? ? ? ? ? }else {
scale = Math.min(container.width() / srcWidth, container.height() / srcHeight);
? ? ? ? ? ? ? ? }
dx = (container.width() - srcWidth * scale) *0.5f;
? ? ? ? ? ? ? ? dy = (container.height() - srcHeight * scale) *0.5f;
? ? ? ? ? ? ? ? matrix.setScale(scale, scale);
? ? ? ? ? ? ? ? matrix.postTranslate(dx, dy);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else if (ScaleType.FIT_CENTER.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempSrc =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempDst =rectFTake(0, 0, container.width(), container.height());
? ? ? ? ? ? ? ? matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(tempDst);
? ? ? ? ? ? ? ? rectFGiven(tempSrc);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else if (ScaleType.FIT_START.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempSrc =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempDst =rectFTake(0, 0, container.width(), container.height());
? ? ? ? ? ? ? ? matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.START);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(tempDst);
? ? ? ? ? ? ? ? rectFGiven(tempSrc);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else if (ScaleType.FIT_END.equals(scaleType)) {
Matrix matrix =matrixTake();
? ? ? ? ? ? ? ? RectF rect =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempSrc =rectFTake(0, 0, srcWidth, srcHeight);
? ? ? ? ? ? ? ? RectF tempDst =rectFTake(0, 0, container.width(), container.height());
? ? ? ? ? ? ? ? matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.END);
? ? ? ? ? ? ? ? matrix.mapRect(result, rect);
? ? ? ? ? ? ? ? rectFGiven(tempDst);
? ? ? ? ? ? ? ? rectFGiven(tempSrc);
? ? ? ? ? ? ? ? rectFGiven(rect);
? ? ? ? ? ? ? ? matrixGiven(matrix);
? ? ? ? ? ? ? ? result.left += container.left;
? ? ? ? ? ? ? ? result.right += container.left;
? ? ? ? ? ? ? ? result.top += container.top;
? ? ? ? ? ? ? ? result.bottom += container.top;
? ? ? ? ? ? }else {
result.set(container);
? ? ? ? ? ? }
}
}
}