圖片

手勢圖片控件 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);

}

}

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末违诗,一起剝皮案震驚了整個濱河市捧颅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌较雕,老刑警劉巖碉哑,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亮蒋,居然都是意外死亡扣典,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門慎玖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贮尖,“玉大人,你說我怎么就攤上這事趁怔∈酰” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵润努,是天一觀的道長关斜。 經(jīng)常有香客問我,道長铺浇,這世上最難降的妖魔是什么痢畜? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮鳍侣,結(jié)果婚禮上丁稀,老公的妹妹穿的比我還像新娘。我一直安慰自己倚聚,他們只是感情好线衫,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惑折,像睡著了一般授账。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唬复,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天矗积,我揣著相機(jī)與錄音,去河邊找鬼敞咧。 笑死,一個胖子當(dāng)著我的面吹牛辜腺,可吹牛的內(nèi)容都是我干的休建。 我是一名探鬼主播乍恐,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼测砂!你這毒婦竟也來了茵烈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤砌些,失蹤者是張志新(化名)和其女友劉穎呜投,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體存璃,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡仑荐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纵东。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粘招。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖偎球,靈堂內(nèi)的尸體忽然破棺而出洒扎,到底是詐尸還是另有隱情,我是刑警寧澤衰絮,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布袍冷,位于F島的核電站,受9級特大地震影響猫牡,放射性物質(zhì)發(fā)生泄漏难裆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一镊掖、第九天 我趴在偏房一處隱蔽的房頂上張望乃戈。 院中可真熱鬧,春花似錦亩进、人聲如沸症虑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谍憔。三九已至,卻和暖如春主籍,著一層夾襖步出監(jiān)牢的瞬間习贫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工千元, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留苫昌,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓幸海,卻偏偏與公主長得像祟身,于是被迫代替她去往敵國和親奥务。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容