手勢圖片控件 PinchImageView
點擊圖片框架 photoView
packagecom.example.android.test.WidegtView;
importandroid.animation.ValueAnimator;
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Matrix;
importandroid.graphics.PointF;
importandroid.graphics.RectF;
importandroid.util.AttributeSet;
importandroid.view.GestureDetector;
importandroid.view.MotionEvent;
importjava.util.ArrayList;
importjava.util.LinkedList;
importjava.util.List;
importjava.util.Queue;
/**
*手勢圖片控件
*
*@authorclifford
*/
public classPinchImageViewextendsandroid.support.v7.widget.AppCompatImageView {
////////////////////////////////配置參數(shù)////////////////////////////////
/**
*圖片縮放動畫時間
*/
public static final intSCALE_ANIMATOR_DURATION=200;
/**
*慣性動畫衰減參數(shù)
*/
public static final floatFLING_DAMPING_FACTOR=0.9f;
/**
*圖片最大放大比例
*/
private static final floatMAX_SCALE=4f;
////////////////////////////////監(jiān)聽器////////////////////////////////
/**
*外界點擊事件
*
*@see#setOnClickListener(OnClickListener)
*/
privateOnClickListenermOnClickListener;
/**
*外界長按事件
*
*@see#setOnLongClickListener(OnLongClickListener)
*/
privateOnLongClickListenermOnLongClickListener;
@Override
public voidsetOnClickListener(OnClickListener l) {
//默認(rèn)的click會在任何點擊情況下都會觸發(fā)济炎,所以搞成自己的
mOnClickListener= l;
}
@Override
public voidsetOnLongClickListener(OnLongClickListener l) {
//默認(rèn)的long click會在任何長按情況下都會觸發(fā),所以搞成自己的
mOnLongClickListener= l;
}
////////////////////////////////公共狀態(tài)獲取////////////////////////////////
/**
*手勢狀態(tài):自由狀態(tài)
*
*@see#getPinchMode()
*/
public static final intPINCH_MODE_FREE=0;
/**
*手勢狀態(tài):單指滾動狀態(tài)
*
*@see#getPinchMode()
*/
public static final intPINCH_MODE_SCROLL=1;
/**
*手勢狀態(tài):雙指縮放狀態(tài)
*
*@see#getPinchMode()
*/
public static final intPINCH_MODE_SCALE=2;
/**
*外層變換矩陣辐真,如果是單位矩陣冻辩,那么圖片是fit center狀態(tài)
*
*@see#getOuterMatrix(Matrix)
*@see#outerMatrixTo(Matrix, long)
*/
privateMatrixmOuterMatrix=newMatrix();
/**
*矩形遮罩
*
*@see#getMask()
*@see#zoomMaskTo(RectF, long)
*/
privateRectFmMask;
/**
*當(dāng)前手勢狀態(tài)
*
*@see#getPinchMode()
*@see#PINCH_MODE_FREE
*@see#PINCH_MODE_SCROLL
*@see#PINCH_MODE_SCALE
*/
private intmPinchMode=PINCH_MODE_FREE;
/**
*獲取外部變換矩陣.
*
*外部變換矩陣記錄了圖片手勢操作的最終結(jié)果,是相對于圖片fit center狀態(tài)的變換.
*默認(rèn)值為單位矩陣,此時圖片為fit center狀態(tài).
*
*@parammatrix用于填充結(jié)果的對象
*@return如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
*/
publicMatrixgetOuterMatrix(Matrix matrix) {
if(matrix ==null) {
matrix =newMatrix(mOuterMatrix);
}else{
matrix.set(mOuterMatrix);
}
returnmatrix;
}
/**
*獲取內(nèi)部變換矩陣.
*
*內(nèi)部變換矩陣是原圖到fit center狀態(tài)的變換,當(dāng)原圖尺寸變化或者控件大小變化都會發(fā)生改變
*當(dāng)尚未布局或者原圖不存在時,其值無意義.所以在調(diào)用前需要確保前置條件有效,否則將影響計算結(jié)果.
*
*@parammatrix用于填充結(jié)果的對象
*@return如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
*/
publicMatrixgetInnerMatrix(Matrix matrix) {
if(matrix ==null) {
matrix =newMatrix();
}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);
}
returnmatrix;
}
/**
*獲取圖片總變換矩陣.
*
*總變換矩陣為內(nèi)部變換矩陣x外部變換矩陣,決定了原圖到所見最終狀態(tài)的變換
*當(dāng)尚未布局或者原圖不存在時,其值無意義.所以在調(diào)用前需要確保前置條件有效,否則將影響計算結(jié)果.
*
*@parammatrix用于填充結(jié)果的對象
*@return如果傳了matrix參數(shù)則將matrix填充后返回,否則new一個填充返回
*
*@see#getOuterMatrix(Matrix)
*@see#getInnerMatrix(Matrix)
*/
publicMatrixgetCurrentImageMatrix(Matrix matrix) {
//獲取內(nèi)部變換矩陣
matrix = getInnerMatrix(matrix);
//乘上外部變換矩陣
matrix.postConcat(mOuterMatrix);
returnmatrix;
}
/**
*獲取當(dāng)前變換后的圖片位置和尺寸
*
*當(dāng)尚未布局或者原圖不存在時,其值無意義.所以在調(diào)用前需要確保前置條件有效,否則將影響計算結(jié)果.
*
*@paramrectF用于填充結(jié)果的對象
*@return如果傳了rectF參數(shù)則將rectF填充后返回,否則new一個填充返回
*
*@see#getCurrentImageMatrix(Matrix)
*/
publicRectFgetImageBound(RectF rectF) {
if(rectF ==null) {
rectF =newRectF();
}else{
rectF.setEmpty();
}
if(!isReady()) {
returnrectF;
}else{
//申請一個空matrix
Matrix matrix = MathUtils.matrixTake();
//獲取當(dāng)前總變換矩陣
getCurrentImageMatrix(matrix);
//對原圖矩形進(jìn)行變換得到當(dāng)前顯示矩形
rectF.set(0,0,getDrawable().getIntrinsicWidth(),getDrawable().getIntrinsicHeight());
matrix.mapRect(rectF);
//釋放臨時matrix
MathUtils.matrixGiven(matrix);
returnrectF;
}
}
/**
*獲取當(dāng)前設(shè)置的mask
*
*@return返回當(dāng)前的mask對象副本,如果當(dāng)前沒有設(shè)置mask則返回null
*/
publicRectFgetMask() {
if(mMask!=null) {
return newRectF(mMask);
}else{
return null;
}
}
/**
*獲取當(dāng)前手勢狀態(tài)
*
*@see#PINCH_MODE_FREE
*@see#PINCH_MODE_SCROLL
*@see#PINCH_MODE_SCALE
*/
public intgetPinchMode() {
returnmPinchMode;
}
/**
*與ViewPager結(jié)合的時候使用
*@paramdirection
*@return
*/
@Override
public booleancanScrollHorizontally(intdirection) {
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) {
returnbound.right> getWidth();
}else{
returnbound.left<0;
}
}
/**
*與ViewPager結(jié)合的時候使用
*@paramdirection
*@return
*/
@Override
public booleancanScrollVertically(intdirection) {
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) {
returnbound.bottom> getHeight();
}else{
returnbound.top<0;
}
}
////////////////////////////////公共狀態(tài)設(shè)置////////////////////////////////
/**
*執(zhí)行當(dāng)前outerMatrix到指定outerMatrix漸變的動畫
*
*調(diào)用此方法會停止正在進(jìn)行中的手勢以及手勢動畫.
*當(dāng)duration為0時,outerMatrix值會被立即設(shè)置而不會啟動動畫.
*
*@paramendMatrix動畫目標(biāo)矩陣
*@paramduration動畫持續(xù)時間
*
*@see#getOuterMatrix(Matrix)
*/
public voidouterMatrixTo(Matrix endMatrix, longduration) {
if(endMatrix ==null) {
return;
}
//將手勢設(shè)置為PINCH_MODE_FREE將停止后續(xù)手勢的觸發(fā)
mPinchMode=PINCH_MODE_FREE;
//停止所有正在進(jìn)行的動畫
cancelAllAnimator();
//如果時間不合法立即執(zhí)行結(jié)果
if(duration <=0) {
mOuterMatrix.set(endMatrix);
dispatchOuterMatrixChanged();
invalidate();
}else{
//創(chuàng)建矩陣變化動畫
mScaleAnimator=newScaleAnimator(mOuterMatrix,endMatrix,duration);
mScaleAnimator.start();
}
}
/**
*執(zhí)行當(dāng)前mask到指定mask的變化動畫
*
*調(diào)用此方法不會停止手勢以及手勢相關(guān)動畫,但會停止正在進(jìn)行的mask動畫.
*當(dāng)前mask為null時,則不執(zhí)行動畫立即設(shè)置為目標(biāo)mask.
*當(dāng)duration為0時,立即將當(dāng)前mask設(shè)置為目標(biāo)mask,不會執(zhí)行動畫.
*
*@parammask動畫目標(biāo)mask
*@paramduration動畫持續(xù)時間
*
*@see#getMask()
*/
public voidzoomMaskTo(RectF mask, longduration) {
if(mask ==null) {
return;
}
//停止mask動畫
if(mMaskAnimator!=null) {
mMaskAnimator.cancel();
mMaskAnimator=null;
}
//如果duration為0或者之前沒有設(shè)置過mask,不執(zhí)行動畫,立即設(shè)置
if(duration <=0||mMask==null) {
if(mMask==null) {
mMask=newRectF();
}
mMask.set(mask);
invalidate();
}else{
//執(zhí)行mask動畫
mMaskAnimator=newMaskAnimator(mMask,mask,duration);
mMaskAnimator.start();
}
}
/**
*重置所有狀態(tài)
*
*重置位置到fit center狀態(tài),清空mask,停止所有手勢,停止所有動畫.
*但不清空drawable,以及事件綁定相關(guān)數(shù)據(jù).
*/
public voidreset() {
//重置位置到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 interfaceOuterMatrixChangedListener {
/**
*外部矩陣變化回調(diào)
*
*外部矩陣的任何變化后都收到此回調(diào).
*外部矩陣變化后,總變化矩陣,圖片的展示位置都將發(fā)生變化.
*
*@parampinchImageView
*
*@see#getOuterMatrix(Matrix)
*@see#getCurrentImageMatrix(Matrix)
*@see#getImageBound(RectF)
*/
voidonOuterMatrixChanged(PinchImageView pinchImageView);
}
/**
*所有OuterMatrixChangedListener監(jiān)聽列表
*
*@see#addOuterMatrixChangedListener(OuterMatrixChangedListener)
*@see#removeOuterMatrixChangedListener(OuterMatrixChangedListener)
*/
privateListmOuterMatrixChangedListeners;
/**
*當(dāng)mOuterMatrixChangedListeners被鎖定不允許修改時,臨時將修改寫到這個副本中
*
*@see#mOuterMatrixChangedListeners
*/
privateListmOuterMatrixChangedListenersCopy;
/**
* mOuterMatrixChangedListeners的修改鎖定
*
*當(dāng)進(jìn)入dispatchOuterMatrixChanged方法時,被加1,退出前被減1
*
*@see#dispatchOuterMatrixChanged()
*@see#addOuterMatrixChangedListener(OuterMatrixChangedListener)
*@see#removeOuterMatrixChangedListener(OuterMatrixChangedListener)
*/
private intmDispatchOuterMatrixChangedLock;
/**
*添加外部矩陣變化監(jiān)聽
*
*@paramlistener
*/
public voidaddOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if(listener ==null) {
return;
}
//如果監(jiān)聽列表沒有被修改鎖定直接將監(jiān)聽添加到監(jiān)聽列表
if(mDispatchOuterMatrixChangedLock==0) {
if(mOuterMatrixChangedListeners==null) {
mOuterMatrixChangedListeners=newArrayList();
}
mOuterMatrixChangedListeners.add(listener);
}else{
//如果監(jiān)聽列表修改被鎖定,那么嘗試在監(jiān)聽列表副本上添加
//監(jiān)聽列表副本將會在鎖定被解除時替換到監(jiān)聽列表里
if(mOuterMatrixChangedListenersCopy==null) {
if(mOuterMatrixChangedListeners!=null) {
mOuterMatrixChangedListenersCopy=newArrayList(mOuterMatrixChangedListeners);
}else{
mOuterMatrixChangedListenersCopy=newArrayList();
}
}
mOuterMatrixChangedListenersCopy.add(listener);
}
}
/**
*刪除外部矩陣變化監(jiān)聽
*
*@paramlistener
*/
public voidremoveOuterMatrixChangedListener(OuterMatrixChangedListener listener) {
if(listener ==null) {
return;
}
//如果監(jiān)聽列表沒有被修改鎖定直接在監(jiān)聽列表數(shù)據(jù)結(jié)構(gòu)上修改
if(mDispatchOuterMatrixChangedLock==0) {
if(mOuterMatrixChangedListeners!=null) {
mOuterMatrixChangedListeners.remove(listener);
}
}else{
//如果監(jiān)聽列表被修改鎖定,那么就在其副本上修改
//其副本將會在鎖定解除時替換回監(jiān)聽列表
if(mOuterMatrixChangedListenersCopy==null) {
if(mOuterMatrixChangedListeners!=null) {
mOuterMatrixChangedListenersCopy=newArrayList(mOuterMatrixChangedListeners);
}
}
if(mOuterMatrixChangedListenersCopy!=null) {
mOuterMatrixChangedListenersCopy.remove(listener);
}
}
}
/**
*觸發(fā)外部矩陣修改事件
*
*需要在每次給外部矩陣設(shè)置值時都調(diào)用此方法.
*
*@see#mOuterMatrix
*/
private voiddispatchOuterMatrixChanged() {
if(mOuterMatrixChangedListeners==null) {
return;
}
//增加鎖
//這里之所以用計數(shù)器做鎖定是因為可能在鎖定期間又間接調(diào)用了此方法產(chǎn)生遞歸
//使用boolean無法判斷遞歸結(jié)束
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 floatgetMaxScale() {
returnMAX_SCALE;
}
/**
*計算雙擊之后圖片接下來應(yīng)該被縮放的比例
*
*如果值大于getMaxScale或者小于fit center尺寸,則實際使用取邊界值.
*通過覆蓋此方法可以定制不同的圖片被雙擊時使用不同的放大策略.
*
*@paraminnerScale當(dāng)前內(nèi)部矩陣的縮放值
*@paramouterScale當(dāng)前外部矩陣的縮放值
*@return接下來的縮放比例
*
*@see#doubleTap(float, float)
*@see#getMaxScale()
*/
protected floatcalculateNextScale(floatinnerScale, floatouterScale) {
floatcurrentScale = innerScale * outerScale;
if(currentScale
returnMAX_SCALE;
}else{
returninnerScale;
}
}
////////////////////////////////初始化////////////////////////////////
publicPinchImageView(Context context) {
super(context);
initView();
}
publicPinchImageView(Context context,AttributeSet attrs) {
super(context,attrs);
initView();
}
publicPinchImageView(Context context,AttributeSet attrs, intdefStyle) {
super(context,attrs,defStyle);
initView();
}
private voidinitView() {
//強(qiáng)制設(shè)置圖片scaleType為matrix
super.setScaleType(ScaleType.MATRIX);
}
//不允許設(shè)置scaleType拆祈,只能用內(nèi)部設(shè)置的matrix
@Override
public voidsetScaleType(ScaleType scaleType) {}
////////////////////////////////繪制////////////////////////////////
@Override
protected voidonDraw(Canvas canvas) {
//在繪制前設(shè)置變換矩陣
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);
}
}
////////////////////////////////有效性判斷////////////////////////////////
/**
*判斷當(dāng)前情況是否能執(zhí)行手勢相關(guān)計算
*
*包括:是否有圖片,圖片是否有尺寸,控件是否有尺寸.
*
*@return是否能執(zhí)行手勢相關(guān)計算
*/
private booleanisReady() {
returngetDrawable() !=null&& getDrawable().getIntrinsicWidth() >0&& getDrawable().getIntrinsicHeight() >0
&& getWidth() >0&& getHeight() >0;
}
////////////////////////////////mask動畫處理////////////////////////////////
/**
* mask修改的動畫
*
*和圖片的動畫相互獨立.
*
*@see#zoomMaskTo(RectF, long)
*/
privateMaskAnimatormMaskAnimator;
/**
* mask變換動畫
*
*將mask從一個rect動畫到另外一個rect
*/
private classMaskAnimatorextendsValueAnimatorimplementsValueAnimator.AnimatorUpdateListener {
/**
*開始mask
*/
private float[]mStart=new float[4];
/**
*結(jié)束mask
*/
private float[]mEnd=new float[4];
/**
*中間結(jié)果mask
*/
private float[]mResult=new float[4];
/**
*創(chuàng)建mask變換動畫
*
*@paramstart動畫起始狀態(tài)
*@paramend動畫終點狀態(tài)
*@paramduration動畫持續(xù)時間
*/
publicMaskAnimator(RectF start,RectF end, longduration) {
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 voidonAnimationUpdate(ValueAnimator animation) {
//獲取動畫進(jìn)度,0-1范圍
floatvalue = (Float) animation.getAnimatedValue();
//根據(jù)進(jìn)度對起點終點之間做插值
for(inti =0;i <4;i++) {
mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;
}
//期間mask有可能被置空了,所以判斷一下
if(mMask==null) {
mMask=newRectF();
}
//設(shè)置新的mask并繪制
mMask.set(mResult[0],mResult[1],mResult[2],mResult[3]);
invalidate();
}
}
////////////////////////////////手勢動畫處理////////////////////////////////
/**
*在單指模式下:
*記錄上一次手指的位置,用于計算新的位置和上一次位置的差值.
*
*雙指模式下:
*記錄兩個手指的中點,作為和mScaleCenter綁定的點.
*這個綁定可以保證mScaleCenter無論如何都會跟隨這個中點.
*
*@see#mScaleCenter
*@see#scale(PointF, float, float, PointF)
*@see#scaleEnd()
*/
privatePointFmLastMovePoint=newPointF();
/**
*縮放模式下圖片的縮放中點.
*
*為其指代的點經(jīng)過innerMatrix變換之后的值.
*其指代的點在手勢過程中始終跟隨mLastMovePoint.
*通過雙指縮放時,其為縮放中心點.
*
*@see#saveScaleContext(float, float, float, float)
*@see#mLastMovePoint
*@see#scale(PointF, float, float, PointF)
*/
privatePointFmScaleCenter=newPointF();
/**
*縮放模式下的基礎(chǔ)縮放比例
*
*為外層縮放值除以開始縮放時兩指距離.
*其值乘上最新的兩指之間距離為最新的圖片縮放比例.
*
*@see#saveScaleContext(float, float, float, float)
*@see#scale(PointF, float, float, PointF)
*/
private floatmScaleBase=0;
/**
*圖片縮放動畫
*
*縮放模式把圖片的位置大小超出限制之后觸發(fā).
*雙擊圖片放大或縮小時觸發(fā).
*手動調(diào)用outerMatrixTo觸發(fā).
*
*@see#scaleEnd()
*@see#doubleTap(float, float)
*@see#outerMatrixTo(Matrix, long)
*/
privateScaleAnimatormScaleAnimator;
/**
*滑動產(chǎn)生的慣性動畫
*
*@see#fling(float, float)
*/
privateFlingAnimatormFlingAnimator;
/**
*常用手勢處理
*
*在onTouchEvent末尾被執(zhí)行.
*/
privateGestureDetectormGestureDetector=newGestureDetector(PinchImageView.this.getContext(), newGestureDetector.SimpleOnGestureListener() {
public booleanonFling(MotionEvent e1,MotionEvent e2, floatvelocityX, floatvelocityY) {
//只有在單指模式結(jié)束之后才允許執(zhí)行fling
if(mPinchMode==PINCH_MODE_FREE&& !(mScaleAnimator!=null&&mScaleAnimator.isRunning())) {
fling(velocityX,velocityY);
}
return true;
}
public voidonLongPress(MotionEvent e) {
//觸發(fā)長按
if(mOnLongClickListener!=null) {
mOnLongClickListener.onLongClick(PinchImageView.this);
}
}
public booleanonDoubleTap(MotionEvent e) {
//當(dāng)手指快速第二次按下觸發(fā),此時必須是單指模式才允許執(zhí)行doubleTap
if(mPinchMode==PINCH_MODE_SCROLL&& !(mScaleAnimator!=null&&mScaleAnimator.isRunning())) {
doubleTap(e.getX(),e.getY());
}
return true;
}
public booleanonSingleTapConfirmed(MotionEvent e) {
//觸發(fā)點擊
if(mOnClickListener!=null) {
mOnClickListener.onClick(PinchImageView.this);
}
return true;
}
});
@Override
public booleanonTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
intaction = event.getAction() & MotionEvent.ACTION_MASK;
//最后一個點抬起或者取消恨闪,結(jié)束所有模式
if(action == MotionEvent.ACTION_UP|| action == MotionEvent.ACTION_CANCEL) {
//如果之前是縮放模式,還需要觸發(fā)一下縮放結(jié)束動畫
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) {
//如果還沒結(jié)束縮放模式,但是第一個點抬起了淤年,那么讓第二個點和第三個點作為縮放控制點
if(event.getAction() >>8==0) {
saveScaleContext(event.getX(1),event.getY(1),event.getX(2),event.getY(2));
//如果還沒結(jié)束縮放模式钧敞,但是第二個點抬起了,那么讓第一個點和第三個點作為縮放控制點
}else if(event.getAction() >>8==1) {
saveScaleContext(event.getX(0),event.getY(0),event.getX(2),event.getY(2));
}
}
//如果抬起的點等于2,那么此時只剩下一個點,也不允許進(jìn)入單指模式,因為此時可能圖片沒有在正確的位置上
}
//第一個點按下麸粮,開啟滾動模式溉苛,記錄開始滾動的點
}else if(action == MotionEvent.ACTION_DOWN) {
//在矩陣動畫過程中不允許啟動滾動模式
if(!(mScaleAnimator!=null&&mScaleAnimator.isRunning())) {
//停止所有動畫
cancelAllAnimator();
//切換到滾動模式
mPinchMode=PINCH_MODE_SCROLL;
//保存觸發(fā)點用于move計算差值
mLastMovePoint.set(event.getX(),event.getY());
}
//非第一個點按下,關(guān)閉滾動模式弄诲,開啟縮放模式愚战,記錄縮放模式的一些初始數(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) {
//每次移動產(chǎn)生一個差值累積到圖片位置上
scrollBy(event.getX() -mLastMovePoint.x,event.getY() -mLastMovePoint.y);
//記錄新的移動點
mLastMovePoint.set(event.getX(),event.getY());
//在縮放模式下移動
}else if(mPinchMode==PINCH_MODE_SCALE&& event.getPointerCount() >1) {
//兩個縮放點間的距離
floatdistance = 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;
}
/**
*讓圖片移動一段距離
*
*不能移動超過可移動范圍,超過了就到可移動范圍邊界為止.
*
*@paramxDiff移動距離
*@paramyDiff移動距離
*@return是否改變了位置
*/
private booleanscrollBy(floatxDiff, floatyDiff) {
if(!isReady()) {
return false;
}
//原圖方框
RectF bound = MathUtils.rectFTake();
getImageBound(bound);
//控件大小
floatdisplayWidth = getWidth();
floatdisplayHeight = getHeight();
//如果當(dāng)前圖片寬度小于控件寬度,則不能移動
if(bound.right- bound.left< displayWidth) {
xDiff =0;
//如果圖片左邊在移動后超出控件左邊
}else if(bound.left+ xDiff >0) {
//如果在移動之前是沒超出的齐遵,計算應(yīng)該移動的距離
if(bound.left<0) {
xDiff = -bound.left;
//否則無法移動
}else{
xDiff =0;
}
//如果圖片右邊在移動后超出控件右邊
}else if(bound.right+ xDiff < displayWidth) {
//如果在移動之前是沒超出的寂玲,計算應(yīng)該移動的距離
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);
//應(yīng)用移動變換
mOuterMatrix.postTranslate(xDiff,yDiff);
dispatchOuterMatrixChanged();
//觸發(fā)重繪
invalidate();
//檢查是否有變化
if(xDiff !=0|| yDiff !=0) {
return true;
}else{
return false;
}
}
/**
*記錄縮放前的一些信息
*
*保存基礎(chǔ)縮放值.
*保存圖片縮放中點.
*
*@paramx1縮放第一個手指
*@paramy1縮放第一個手指
*@paramx2縮放第二個手指
*@paramy2縮放第二個手指
*/
private voidsaveScaleContext(floatx1, floaty1, floatx2, floaty2) {
//記錄基礎(chǔ)縮放值,其中圖片縮放比例按照x方向來計算
//理論上圖片應(yīng)該是等比的,x和y方向比例相同
//但是有可能外部設(shè)定了不規(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)下進(jìn)行變換
//所以需要把兩手指中點除以外層變換矩陣得到mScaleCenter
float[] center = MathUtils.inverseMatrixPoint(MathUtils.getCenterPoint(x1,y1,x2,y2),mOuterMatrix);
mScaleCenter.set(center[0],center[1]);
}
/**
*對圖片按照一些手勢信息進(jìn)行縮放
*
*@paramscaleCentermScaleCenter
*@paramscaleBasemScaleBase
*@paramdistance手指兩點之間距離
*@paramlineCenter手指兩點之間中點
*
*@see#mScaleCenter
*@see#mScaleBase
*/
private voidscale(PointF scaleCenter, floatscaleBase, floatdistance,PointF lineCenter) {
if(!isReady()) {
return;
}
//計算圖片從fit center狀態(tài)到目標(biāo)狀態(tài)的縮放比例
floatscale = scaleBase * distance;
Matrix matrix = MathUtils.matrixTake();
//按照圖片縮放中心縮放,并且讓縮放中心在縮放點中點上
matrix.postScale(scale,scale,scaleCenter.x,scaleCenter.y);
//讓圖片的縮放中點跟隨手指縮放中點
matrix.postTranslate(lineCenter.x- scaleCenter.x,lineCenter.y- scaleCenter.y);
//應(yīng)用變換
mOuterMatrix.set(matrix);
MathUtils.matrixGiven(matrix);
dispatchOuterMatrixChanged();
//重繪
invalidate();
}
/**
*雙擊后放大或者縮小
*
*將圖片縮放比例縮放到nextScale指定的值.
*但nextScale值不能大于最大縮放值不能小于fit center情況下的縮放值.
*將雙擊的點盡量移動到控件中心.
*
*@paramx雙擊的點
*@paramy雙擊的點
*
*@see#calculateNextScale(float, float)
*@see#getMaxScale()
*/
private voiddoubleTap(floatx, floaty) {
if(!isReady()) {
return;
}
//獲取第一層變換矩陣
Matrix innerMatrix = MathUtils.matrixTake();
getInnerMatrix(innerMatrix);
//當(dāng)前總的縮放比例
floatinnerScale = MathUtils.getMatrixScale(innerMatrix)[0];
floatouterScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
floatcurrentScale = innerScale * outerScale;
//控件大小
floatdisplayWidth = getWidth();
floatdisplayHeight = getHeight();
//最大放大大小
floatmaxScale = getMaxScale();
//接下來要放大的大小
floatnextScale = calculateNextScale(innerScale,outerScale);
//如果接下來放大大于最大值或者小于fit center值梗摇,則取邊界
if(nextScale > maxScale) {
nextScale = maxScale;
}
if(nextScale < innerScale) {
nextScale = innerScale;
}
//開始計算縮放動畫的結(jié)果矩陣
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);
//修正位置
floatpostX =0;
floatpostY =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;
}
//應(yīng)用修正位置
animEnd.postTranslate(postX,postY);
//清理當(dāng)前可能正在執(zhí)行的動畫
cancelAllAnimator();
//啟動矩陣動畫
mScaleAnimator=newScaleAnimator(mOuterMatrix,animEnd);
mScaleAnimator.start();
//清理臨時變量
MathUtils.rectFGiven(testBound);
MathUtils.matrixGiven(testMatrix);
MathUtils.matrixGiven(animEnd);
MathUtils.matrixGiven(innerMatrix);
}
/**
*當(dāng)縮放操作結(jié)束動畫
*
*如果圖片超過邊界,找到最近的位置動畫恢復(fù).
*如果圖片縮放尺寸超過最大值或者最小值,找到最近的值動畫恢復(fù).
*/
private voidscaleEnd() {
if(!isReady()) {
return;
}
//是否修正了位置
booleanchange =false;
//獲取圖片整體的變換矩陣
Matrix currentMatrix = MathUtils.matrixTake();
getCurrentImageMatrix(currentMatrix);
//整體縮放比例
floatcurrentScale = MathUtils.getMatrixScale(currentMatrix)[0];
//第二層縮放比例
floatouterScale = MathUtils.getMatrixScale(mOuterMatrix)[0];
//控件大小
floatdisplayWidth = getWidth();
floatdisplayHeight = getHeight();
//最大縮放比例
floatmaxScale = getMaxScale();
//比例修正
floatscalePost =1f;
//位置修正
floatpostX =0;
floatpostY =0;
//如果整體縮放比例大于最大比例拓哟,進(jìn)行縮放修正
if(currentScale > maxScale) {
scalePost = maxScale / currentScale;
}
//如果縮放修正后整體導(dǎo)致第二層縮放小于1(就是圖片比fit center狀態(tài)還小)伶授,重新修正縮放
if(outerScale * scalePost <1f) {
scalePost =1f/ outerScale;
}
//如果縮放修正不為1断序,說明進(jìn)行了修正
if(scalePost !=1f) {
change =true;
}
//嘗試根據(jù)縮放點進(jìn)行縮放修正
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);
//檢測縮放修正后位置有無超出流纹,如果超出進(jìn)行位置修正
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,說明進(jìn)行了修正
if(postX !=0|| postY !=0) {
change =true;
}
//只有有執(zhí)行修正才執(zhí)行動畫
if(change) {
//計算結(jié)束矩陣
Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);
animEnd.postScale(scalePost,scalePost,mLastMovePoint.x,mLastMovePoint.y);
animEnd.postTranslate(postX,postY);
//清理當(dāng)前可能正在執(zhí)行的動畫
cancelAllAnimator();
//啟動矩陣動畫
mScaleAnimator=newScaleAnimator(mOuterMatrix,animEnd);
mScaleAnimator.start();
//清理臨時變量
MathUtils.matrixGiven(animEnd);
}
//清理臨時變量
MathUtils.rectFGiven(testBound);
MathUtils.matrixGiven(testMatrix);
MathUtils.matrixGiven(currentMatrix);
}
/**
*執(zhí)行慣性動畫
*
*動畫在遇到不能移動就停止.
*動畫速度衰減到很小就停止.
*
*其中參數(shù)速度單位為 像素/秒
*
*@paramvxx方向速度
*@paramvyy方向速度
*/
private voidfling(floatvx, floatvy) {
if(!isReady()) {
return;
}
//清理當(dāng)前可能正在執(zhí)行的動畫
cancelAllAnimator();
//創(chuàng)建慣性動畫
//FlingAnimator單位為 像素/幀,一秒60幀
mFlingAnimator=newFlingAnimator(vx /60f,vy /60f);
mFlingAnimator.start();
}
/**
*停止所有手勢動畫
*/
private voidcancelAllAnimator() {
if(mScaleAnimator!=null) {
mScaleAnimator.cancel();
mScaleAnimator=null;
}
if(mFlingAnimator!=null) {
mFlingAnimator.cancel();
mFlingAnimator=null;
}
}
/**
*慣性動畫
*
*速度逐漸衰減,每幀速度衰減為原來的FLING_DAMPING_FACTOR,當(dāng)速度衰減到小于1時停止.
*當(dāng)圖片不能移動時,動畫停止.
*/
private classFlingAnimatorextendsValueAnimatorimplementsValueAnimator.AnimatorUpdateListener {
/**
*速度向量
*/
private float[]mVector;
/**
*創(chuàng)建慣性動畫
*
*參數(shù)單位為 像素/幀
*
*@paramvectorX速度向量
*@paramvectorY速度向量
*/
publicFlingAnimator(floatvectorX, floatvectorY) {
super();
setFloatValues(0,1f);
setDuration(1000000);
addUpdateListener(this);
mVector=new float[]{vectorX,vectorY};
}
@Override
public voidonAnimationUpdate(ValueAnimator animation) {
//移動圖像并給出結(jié)果
booleanresult = scrollBy(mVector[0],mVector[1]);
//衰減速度
mVector[0] *=FLING_DAMPING_FACTOR;
mVector[1] *=FLING_DAMPING_FACTOR;
//速度太小或者不能移動了就結(jié)束
if(!result || MathUtils.getDistance(0,0,mVector[0],mVector[1]) <1f) {
animation.cancel();
}
}
}
/**
*縮放動畫
*
*在給定時間內(nèi)從一個矩陣的變化逐漸動畫到另一個矩陣的變化
*/
private classScaleAnimatorextendsValueAnimatorimplementsValueAnimator.AnimatorUpdateListener {
/**
*開始矩陣
*/
private float[]mStart=new float[9];
/**
*結(jié)束矩陣
*/
private float[]mEnd=new float[9];
/**
*中間結(jié)果矩陣
*/
private float[]mResult=new float[9];
/**
*構(gòu)建一個縮放動畫
*
*從一個矩陣變換到另外一個矩陣
*
*@paramstart開始矩陣
*@paramend結(jié)束矩陣
*/
publicScaleAnimator(Matrix start,Matrix end) {
this(start,end,SCALE_ANIMATOR_DURATION);
}
/**
*構(gòu)建一個縮放動畫
*
*從一個矩陣變換到另外一個矩陣
*
*@paramstart開始矩陣
*@paramend結(jié)束矩陣
*@paramduration動畫時間
*/
publicScaleAnimator(Matrix start,Matrix end, longduration) {
super();
setFloatValues(0,1f);
setDuration(duration);
addUpdateListener(this);
start.getValues(mStart);
end.getValues(mEnd);
}
@Override
public voidonAnimationUpdate(ValueAnimator animation) {
//獲取動畫進(jìn)度
floatvalue = (Float) animation.getAnimatedValue();
//根據(jù)動畫進(jìn)度計算矩陣中間插值
for(inti =0;i <9;i++) {
mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;
}
//設(shè)置矩陣并重繪
mOuterMatrix.setValues(mResult);
dispatchOuterMatrixChanged();
invalidate();
}
}
////////////////////////////////防止內(nèi)存抖動復(fù)用對象////////////////////////////////
/**
*對象池
*
*防止頻繁new對象產(chǎn)生內(nèi)存抖動.
*由于對象池最大長度限制,如果吞度量超過對象池容量,仍然會發(fā)生抖動.
*此時需要增大對象池容量,但是會占用更多內(nèi)存.
*
*@param對象池容納的對象類型
*/
private static abstract classObjectsPool {
/**
*對象池的最大容量
*/
private intmSize;
/**
*對象池隊列
*/
privateQueuemQueue;
/**
*創(chuàng)建一個對象池
*
*@paramsize對象池最大容量
*/
publicObjectsPool(intsize) {
mSize= size;
mQueue=newLinkedList();
}
/**
*獲取一個空閑的對象
*
*如果對象池為空,則對象池自己會new一個返回.
*如果對象池內(nèi)有對象,則取一個已存在的返回.
* take出來的對象用完要記得調(diào)用given歸還.
*如果不歸還,讓然會發(fā)生內(nèi)存抖動,但不會引起泄漏.
*
*@return可用的對象
*
*@see#given(Object)
*/
publicTtake() {
//如果池內(nèi)為空就創(chuàng)建一個
if(mQueue.size() ==0) {
returnnewInstance();
}else{
//對象池里有就從頂端拿出來一個返回
returnresetInstance(mQueue.poll());
}
}
/**
*歸還對象池內(nèi)申請的對象
*
*如果歸還的對象數(shù)量超過對象池容量,那么歸還的對象就會被丟棄.
*
*@paramobj歸還的對象
*
*@see#take()
*/
public voidgiven(Tobj) {
//如果對象池還有空位子就歸還對象
if(obj !=null&&mQueue.size()
mQueue.offer(obj);
}
}
/**
*實例化對象
*
*@return創(chuàng)建的對象
*/
abstract protectedTnewInstance();
/**
*重置對象
*
*把對象數(shù)據(jù)清空到就像剛創(chuàng)建的一樣.
*
*@paramobj需要被重置的對象
*@return被重置之后的對象
*/
abstract protectedTresetInstance(Tobj);
}
/**
*矩陣對象池
*/
private static classMatrixPoolextendsObjectsPool {
publicMatrixPool(intsize) {
super(size);
}
@Override
protectedMatrixnewInstance() {
return newMatrix();
}
@Override
protectedMatrixresetInstance(Matrix obj) {
obj.reset();
returnobj;
}
}
/**
*矩形對象池
*/
private static classRectFPoolextendsObjectsPool {
publicRectFPool(intsize) {
super(size);
}
@Override
protectedRectFnewInstance() {
return newRectF();
}
@Override
protectedRectFresetInstance(RectF obj) {
obj.setEmpty();
returnobj;
}
}
////////////////////////////////數(shù)學(xué)計算工具類////////////////////////////////
/**
*數(shù)學(xué)計算工具類
*/
public static classMathUtils {
/**
*矩陣對象池
*/
private staticMatrixPoolmMatrixPool=newMatrixPool(16);
/**
*獲取矩陣對象
*/
public staticMatrixmatrixTake() {
returnmMatrixPool.take();
}
/**
*獲取某個矩陣的copy
*/
public staticMatrixmatrixTake(Matrix matrix) {
Matrix result =mMatrixPool.take();
if(matrix !=null) {
result.set(matrix);
}
returnresult;
}
/**
*歸還矩陣對象
*/
public static voidmatrixGiven(Matrix matrix) {
mMatrixPool.given(matrix);
}
/**
*矩形對象池
*/
private staticRectFPoolmRectFPool=newRectFPool(16);
/**
*獲取矩形對象
*/
public staticRectFrectFTake() {
returnmRectFPool.take();
}
/**
*按照指定值獲取矩形對象
*/
public staticRectFrectFTake(floatleft, floattop, floatright, floatbottom) {
RectF result =mRectFPool.take();
result.set(left,top,right,bottom);
returnresult;
}
/**
*獲取某個矩形的副本
*/
public staticRectFrectFTake(RectF rectF) {
RectF result =mRectFPool.take();
if(rectF !=null) {
result.set(rectF);
}
returnresult;
}
/**
*歸還矩形對象
*/
public static voidrectFGiven(RectF rectF) {
mRectFPool.given(rectF);
}
/**
*獲取兩點之間距離
*
*@paramx1點1
*@paramy1點1
*@paramx2點2
*@paramy2點2
*@return距離
*/
public static floatgetDistance(floatx1, floaty1, floatx2, floaty2) {
floatx = x1 - x2;
floaty = y1 - y2;
return(float) Math.sqrt(x * x + y * y);
}
/**
*獲取兩點的中點
*
*@paramx1點1
*@paramy1點1
*@paramx2點2
*@paramy2點2
*@returnfloat[]{x, y}
*/
public static float[]getCenterPoint(floatx1, floaty1, floatx2, floaty2) {
return new float[]{(x1 + x2) /2f,(y1 + y2) /2f};
}
/**
*獲取矩陣的縮放值
*
*@parammatrix要計算的矩陣
*@returnfloat[]{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的值.
*
*@parampoint
*@parammatrix
*@returnunknownPoint
*/
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就是結(jié)果
inverse.mapPoints(dst,point);
//清除臨時變量
matrixGiven(inverse);
returndst;
}else{
return new float[2];
}
}
/**
*計算兩個矩形之間的變換矩陣
*
* unknownMatrix.mapRect(to, from)
*已知from矩形和to矩形,求unknownMatrix
*
*@paramfrom
*@paramto
*@paramresultunknownMatrix
*/
public static voidcalculateRectTranslateMatrix(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中的顯示矩形
*
*@paramcontainerImageView的Rect
*@paramsrcWidth圖片的寬度
*@paramsrcHeight圖片的高度
*@paramscaleType圖片在ImageView中的ScaleType
*@paramresult圖片應(yīng)該在ImageView中展示的矩形
*/
public static voidcalculateScaledRectInContainer(RectF container, floatsrcWidth, floatsrcHeight,ScaleType scaleType,RectF result) {
if(container ==null|| result ==null) {
return;
}
if(srcWidth ==0|| srcHeight ==0) {
return;
}
//默認(rèn)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);
floatscale;
floatdx =0;
floatdy =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);
floatscale;
floatdx;
floatdy;
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);
}
}
}
}