图片

手势图片控件 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 {

////////////////////////////////配置参数////////////////////////////////

/**

*图片缩放动画时间

*/

public static final intSCALE_ANIMATOR_DURATION=200;

/**

*惯性动画衰减参数

*/

public static final floatFLING_DAMPING_FACTOR=0.9f;

/**

*图片最大放大比例

*/

private static final floatMAX_SCALE=4f;

////////////////////////////////监听器////////////////////////////////

/**

*外界点击事件

*

*@see#setOnClickListener(OnClickListener)

*/

privateOnClickListenermOnClickListener;

/**

*外界长按事件

*

*@see#setOnLongClickListener(OnLongClickListener)

*/

privateOnLongClickListenermOnLongClickListener;

@Override

public voidsetOnClickListener(OnClickListener l) {

//默认的click会在任何点击情况下都会触发,所以搞成自己的

mOnClickListener= l;

}

@Override

public voidsetOnLongClickListener(OnLongClickListener l) {

//默认的long click会在任何长按情况下都会触发,所以搞成自己的

mOnLongClickListener= l;

}

////////////////////////////////公共状态获取////////////////////////////////

/**

*手势状态:自由状态

*

*@see#getPinchMode()

*/

public static final intPINCH_MODE_FREE=0;

/**

*手势状态:单指滚动状态

*

*@see#getPinchMode()

*/

public static final intPINCH_MODE_SCROLL=1;

/**

*手势状态:双指缩放状态

*

*@see#getPinchMode()

*/

public static final intPINCH_MODE_SCALE=2;

/**

*外层变换矩阵,如果是单位矩阵,那么图片是fit center状态

*

*@see#getOuterMatrix(Matrix)

*@see#outerMatrixTo(Matrix, long)

*/

privateMatrixmOuterMatrix=newMatrix();

/**

*矩形遮罩

*

*@see#getMask()

*@see#zoomMaskTo(RectF, long)

*/

privateRectFmMask;

/**

*当前手势状态

*

*@see#getPinchMode()

*@see#PINCH_MODE_FREE

*@see#PINCH_MODE_SCROLL

*@see#PINCH_MODE_SCALE

*/

private intmPinchMode=PINCH_MODE_FREE;

/**

*获取外部变换矩阵.

*

*外部变换矩阵记录了图片手势操作的最终结果,是相对于图片fit center状态的变换.

*默认值为单位矩阵,此时图片为fit center状态.

*

*@parammatrix用于填充结果的对象

*@return如果传了matrix参数则将matrix填充后返回,否则new一个填充返回

*/

publicMatrixgetOuterMatrix(Matrix matrix) {

if(matrix ==null) {

matrix =newMatrix(mOuterMatrix);

}else{

matrix.set(mOuterMatrix);

}

returnmatrix;

}

/**

*获取内部变换矩阵.

*

*内部变换矩阵是原图到fit center状态的变换,当原图尺寸变化或者控件大小变化都会发生改变

*当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.

*

*@parammatrix用于填充结果的对象

*@return如果传了matrix参数则将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;

}

/**

*获取图片总变换矩阵.

*

*总变换矩阵为内部变换矩阵x外部变换矩阵,决定了原图到所见最终状态的变换

*当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.

*

*@parammatrix用于填充结果的对象

*@return如果传了matrix参数则将matrix填充后返回,否则new一个填充返回

*

*@see#getOuterMatrix(Matrix)

*@see#getInnerMatrix(Matrix)

*/

publicMatrixgetCurrentImageMatrix(Matrix matrix) {

//获取内部变换矩阵

matrix = getInnerMatrix(matrix);

//乘上外部变换矩阵

matrix.postConcat(mOuterMatrix);

returnmatrix;

}

/**

*获取当前变换后的图片位置和尺寸

*

*当尚未布局或者原图不存在时,其值无意义.所以在调用前需要确保前置条件有效,否则将影响计算结果.

*

*@paramrectF用于填充结果的对象

*@return如果传了rectF参数则将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();

//获取当前总变换矩阵

getCurrentImageMatrix(matrix);

//对原图矩形进行变换得到当前显示矩形

rectF.set(0,0,getDrawable().getIntrinsicWidth(),getDrawable().getIntrinsicHeight());

matrix.mapRect(rectF);

//释放临时matrix

MathUtils.matrixGiven(matrix);

returnrectF;

}

}

/**

*获取当前设置的mask

*

*@return返回当前的mask对象副本,如果当前没有设置mask则返回null

*/

publicRectFgetMask() {

if(mMask!=null) {

return newRectF(mMask);

}else{

return null;

}

}

/**

*获取当前手势状态

*

*@see#PINCH_MODE_FREE

*@see#PINCH_MODE_SCROLL

*@see#PINCH_MODE_SCALE

*/

public intgetPinchMode() {

returnmPinchMode;

}

/**

*与ViewPager结合的时候使用

*@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结合的时候使用

*@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;

}

}

////////////////////////////////公共状态设置////////////////////////////////

/**

*执行当前outerMatrix到指定outerMatrix渐变的动画

*

*调用此方法会停止正在进行中的手势以及手势动画.

*当duration为0时,outerMatrix值会被立即设置而不会启动动画.

*

*@paramendMatrix动画目标矩阵

*@paramduration动画持续时间

*

*@see#getOuterMatrix(Matrix)

*/

public voidouterMatrixTo(Matrix endMatrix, longduration) {

if(endMatrix ==null) {

return;

}

//将手势设置为PINCH_MODE_FREE将停止后续手势的触发

mPinchMode=PINCH_MODE_FREE;

//停止所有正在进行的动画

cancelAllAnimator();

//如果时间不合法立即执行结果

if(duration <=0) {

mOuterMatrix.set(endMatrix);

dispatchOuterMatrixChanged();

invalidate();

}else{

//创建矩阵变化动画

mScaleAnimator=newScaleAnimator(mOuterMatrix,endMatrix,duration);

mScaleAnimator.start();

}

}

/**

*执行当前mask到指定mask的变化动画

*

*调用此方法不会停止手势以及手势相关动画,但会停止正在进行的mask动画.

*当前mask为null时,则不执行动画立即设置为目标mask.

*当duration为0时,立即将当前mask设置为目标mask,不会执行动画.

*

*@parammask动画目标mask

*@paramduration动画持续时间

*

*@see#getMask()

*/

public voidzoomMaskTo(RectF mask, longduration) {

if(mask ==null) {

return;

}

//停止mask动画

if(mMaskAnimator!=null) {

mMaskAnimator.cancel();

mMaskAnimator=null;

}

//如果duration为0或者之前没有设置过mask,不执行动画,立即设置

if(duration <=0||mMask==null) {

if(mMask==null) {

mMask=newRectF();

}

mMask.set(mask);

invalidate();

}else{

//执行mask动画

mMaskAnimator=newMaskAnimator(mMask,mask,duration);

mMaskAnimator.start();

}

}

/**

*重置所有状态

*

*重置位置到fit center状态,清空mask,停止所有手势,停止所有动画.

*但不清空drawable,以及事件绑定相关数据.

*/

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();

}

////////////////////////////////对外广播事件////////////////////////////////

/**

*外部矩阵变化事件通知监听器

*/

public interfaceOuterMatrixChangedListener {

/**

*外部矩阵变化回调

*

*外部矩阵的任何变化后都收到此回调.

*外部矩阵变化后,总变化矩阵,图片的展示位置都将发生变化.

*

*@parampinchImageView

*

*@see#getOuterMatrix(Matrix)

*@see#getCurrentImageMatrix(Matrix)

*@see#getImageBound(RectF)

*/

voidonOuterMatrixChanged(PinchImageView pinchImageView);

}

/**

*所有OuterMatrixChangedListener监听列表

*

*@see#addOuterMatrixChangedListener(OuterMatrixChangedListener)

*@see#removeOuterMatrixChangedListener(OuterMatrixChangedListener)

*/

privateListmOuterMatrixChangedListeners;

/**

*当mOuterMatrixChangedListeners被锁定不允许修改时,临时将修改写到这个副本中

*

*@see#mOuterMatrixChangedListeners

*/

privateListmOuterMatrixChangedListenersCopy;

/**

* mOuterMatrixChangedListeners的修改锁定

*

*当进入dispatchOuterMatrixChanged方法时,被加1,退出前被减1

*

*@see#dispatchOuterMatrixChanged()

*@see#addOuterMatrixChangedListener(OuterMatrixChangedListener)

*@see#removeOuterMatrixChangedListener(OuterMatrixChangedListener)

*/

private intmDispatchOuterMatrixChangedLock;

/**

*添加外部矩阵变化监听

*

*@paramlistener

*/

public voidaddOuterMatrixChangedListener(OuterMatrixChangedListener listener) {

if(listener ==null) {

return;

}

//如果监听列表没有被修改锁定直接将监听添加到监听列表

if(mDispatchOuterMatrixChangedLock==0) {

if(mOuterMatrixChangedListeners==null) {

mOuterMatrixChangedListeners=newArrayList();

}

mOuterMatrixChangedListeners.add(listener);

}else{

//如果监听列表修改被锁定,那么尝试在监听列表副本上添加

//监听列表副本将会在锁定被解除时替换到监听列表里

if(mOuterMatrixChangedListenersCopy==null) {

if(mOuterMatrixChangedListeners!=null) {

mOuterMatrixChangedListenersCopy=newArrayList(mOuterMatrixChangedListeners);

}else{

mOuterMatrixChangedListenersCopy=newArrayList();

}

}

mOuterMatrixChangedListenersCopy.add(listener);

}

}

/**

*删除外部矩阵变化监听

*

*@paramlistener

*/

public voidremoveOuterMatrixChangedListener(OuterMatrixChangedListener listener) {

if(listener ==null) {

return;

}

//如果监听列表没有被修改锁定直接在监听列表数据结构上修改

if(mDispatchOuterMatrixChangedLock==0) {

if(mOuterMatrixChangedListeners!=null) {

mOuterMatrixChangedListeners.remove(listener);

}

}else{

//如果监听列表被修改锁定,那么就在其副本上修改

//其副本将会在锁定解除时替换回监听列表

if(mOuterMatrixChangedListenersCopy==null) {

if(mOuterMatrixChangedListeners!=null) {

mOuterMatrixChangedListenersCopy=newArrayList(mOuterMatrixChangedListeners);

}

}

if(mOuterMatrixChangedListenersCopy!=null) {

mOuterMatrixChangedListenersCopy.remove(listener);

}

}

}

/**

*触发外部矩阵修改事件

*

*需要在每次给外部矩阵设置值时都调用此方法.

*

*@see#mOuterMatrix

*/

private voiddispatchOuterMatrixChanged() {

if(mOuterMatrixChangedListeners==null) {

return;

}

//增加锁

//这里之所以用计数器做锁定是因为可能在锁定期间又间接调用了此方法产生递归

//使用boolean无法判断递归结束

mDispatchOuterMatrixChangedLock++;

//在列表循环过程中不允许修改列表,否则将引发崩溃

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;

}

/**

*计算双击之后图片接下来应该被缩放的比例

*

*如果值大于getMaxScale或者小于fit center尺寸,则实际使用取边界值.

*通过覆盖此方法可以定制不同的图片被双击时使用不同的放大策略.

*

*@paraminnerScale当前内部矩阵的缩放值

*@paramouterScale当前外部矩阵的缩放值

*@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() {

//强制设置图片scaleType为matrix

super.setScaleType(ScaleType.MATRIX);

}

//不允许设置scaleType,只能用内部设置的matrix

@Override

public voidsetScaleType(ScaleType scaleType) {}

////////////////////////////////绘制////////////////////////////////

@Override

protected voidonDraw(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);

}

}

////////////////////////////////有效性判断////////////////////////////////

/**

*判断当前情况是否能执行手势相关计算

*

*包括:是否有图片,图片是否有尺寸,控件是否有尺寸.

*

*@return是否能执行手势相关计算

*/

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];

/**

*结束mask

*/

private float[]mEnd=new float[4];

/**

*中间结果mask

*/

private float[]mResult=new float[4];

/**

*创建mask变换动画

*

*@paramstart动画起始状态

*@paramend动画终点状态

*@paramduration动画持续时间

*/

publicMaskAnimator(RectF start,RectF end, longduration) {

super();

setFloatValues(0,1f);

setDuration(duration);

addUpdateListener(this);

//将起点终点拷贝到数组方便计算

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) {

//获取动画进度,0-1范围

floatvalue = (Float) animation.getAnimatedValue();

//根据进度对起点终点之间做插值

for(inti =0;i <4;i++) {

mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;

}

//期间mask有可能被置空了,所以判断一下

if(mMask==null) {

mMask=newRectF();

}

//设置新的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();

/**

*缩放模式下图片的缩放中点.

*

*为其指代的点经过innerMatrix变换之后的值.

*其指代的点在手势过程中始终跟随mLastMovePoint.

*通过双指缩放时,其为缩放中心点.

*

*@see#saveScaleContext(float, float, float, float)

*@see#mLastMovePoint

*@see#scale(PointF, float, float, PointF)

*/

privatePointFmScaleCenter=newPointF();

/**

*缩放模式下的基础缩放比例

*

*为外层缩放值除以开始缩放时两指距离.

*其值乘上最新的两指之间距离为最新的图片缩放比例.

*

*@see#saveScaleContext(float, float, float, float)

*@see#scale(PointF, float, float, PointF)

*/

private floatmScaleBase=0;

/**

*图片缩放动画

*

*缩放模式把图片的位置大小超出限制之后触发.

*双击图片放大或缩小时触发.

*手动调用outerMatrixTo触发.

*

*@see#scaleEnd()

*@see#doubleTap(float, float)

*@see#outerMatrixTo(Matrix, long)

*/

privateScaleAnimatormScaleAnimator;

/**

*滑动产生的惯性动画

*

*@see#fling(float, float)

*/

privateFlingAnimatormFlingAnimator;

/**

*常用手势处理

*

*在onTouchEvent末尾被执行.

*/

privateGestureDetectormGestureDetector=newGestureDetector(PinchImageView.this.getContext(), newGestureDetector.SimpleOnGestureListener() {

public booleanonFling(MotionEvent e1,MotionEvent e2, floatvelocityX, floatvelocityY) {

//只有在单指模式结束之后才允许执行fling

if(mPinchMode==PINCH_MODE_FREE&& !(mScaleAnimator!=null&&mScaleAnimator.isRunning())) {

fling(velocityX,velocityY);

}

return true;

}

public voidonLongPress(MotionEvent e) {

//触发长按

if(mOnLongClickListener!=null) {

mOnLongClickListener.onLongClick(PinchImageView.this);

}

}

public booleanonDoubleTap(MotionEvent e) {

//当手指快速第二次按下触发,此时必须是单指模式才允许执行doubleTap

if(mPinchMode==PINCH_MODE_SCROLL&& !(mScaleAnimator!=null&&mScaleAnimator.isRunning())) {

doubleTap(e.getX(),e.getY());

}

return true;

}

public booleanonSingleTapConfirmed(MotionEvent e) {

//触发点击

if(mOnClickListener!=null) {

mOnClickListener.onClick(PinchImageView.this);

}

return true;

}

});

@Override

public booleanonTouchEvent(MotionEvent event) {

super.onTouchEvent(event);

intaction = event.getAction() & MotionEvent.ACTION_MASK;

//最后一个点抬起或者取消,结束所有模式

if(action == MotionEvent.ACTION_UP|| action == MotionEvent.ACTION_CANCEL) {

//如果之前是缩放模式,还需要触发一下缩放结束动画

if(mPinchMode==PINCH_MODE_SCALE) {

scaleEnd();

}

mPinchMode=PINCH_MODE_FREE;

}else if(action == MotionEvent.ACTION_POINTER_UP) {

//多个手指情况下抬起一个手指,此时需要是缩放模式才触发

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;

//保存触发点用于move计算差值

mLastMovePoint.set(event.getX(),event.getY());

}

//非第一个点按下,关闭滚动模式,开启缩放模式,记录缩放模式的一些初始数据

}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) {

//两个缩放点间的距离

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();

//如果当前图片宽度小于控件宽度,则不能移动

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();

//触发重绘

invalidate();

//检查是否有变化

if(xDiff !=0|| yDiff !=0) {

return true;

}else{

return false;

}

}

/**

*记录缩放前的一些信息

*

*保存基础缩放值.

*保存图片缩放中点.

*

*@paramx1缩放第一个手指

*@paramy1缩放第一个手指

*@paramx2缩放第二个手指

*@paramy2缩放第二个手指

*/

private voidsaveScaleContext(floatx1, floaty1, floatx2, floaty2) {

//记录基础缩放值,其中图片缩放比例按照x方向来计算

//理论上图片应该是等比的,x和y方向比例相同

//但是有可能外部设定了不规范的值.

//但是后续的scale操作会将xy不等的缩放值纠正,改成和x方向相同

mScaleBase= MathUtils.getMatrixScale(mOuterMatrix)[0] / MathUtils.getDistance(x1,y1,x2,y2);

//两手指的中点在屏幕上落在了图片的某个点上,图片上的这个点在经过总矩阵变换后和手指中点相同

//现在我们需要得到图片上这个点在图片是fit center状态下在屏幕上的位置

//因为后续的计算都是基于图片是fit center状态下进行变换

//所以需要把两手指中点除以外层变换矩阵得到mScaleCenter

float[] center = MathUtils.inverseMatrixPoint(MathUtils.getCenterPoint(x1,y1,x2,y2),mOuterMatrix);

mScaleCenter.set(center[0],center[1]);

}

/**

*对图片按照一些手势信息进行缩放

*

*@paramscaleCentermScaleCenter

*@paramscaleBasemScaleBase

*@paramdistance手指两点之间距离

*@paramlineCenter手指两点之间中点

*

*@see#mScaleCenter

*@see#mScaleBase

*/

private voidscale(PointF scaleCenter, floatscaleBase, floatdistance,PointF lineCenter) {

if(!isReady()) {

return;

}

//计算图片从fit center状态到目标状态的缩放比例

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);

//应用变换

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);

//当前总的缩放比例

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;

}

//开始计算缩放动画的结果矩阵

Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);

//计算还需缩放的倍数

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;

}

//应用修正位置

animEnd.postTranslate(postX,postY);

//清理当前可能正在执行的动画

cancelAllAnimator();

//启动矩阵动画

mScaleAnimator=newScaleAnimator(mOuterMatrix,animEnd);

mScaleAnimator.start();

//清理临时变量

MathUtils.rectFGiven(testBound);

MathUtils.matrixGiven(testMatrix);

MathUtils.matrixGiven(animEnd);

MathUtils.matrixGiven(innerMatrix);

}

/**

*当缩放操作结束动画

*

*如果图片超过边界,找到最近的位置动画恢复.

*如果图片缩放尺寸超过最大值或者最小值,找到最近的值动画恢复.

*/

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;

//如果整体缩放比例大于最大比例,进行缩放修正

if(currentScale > maxScale) {

scalePost = maxScale / currentScale;

}

//如果缩放修正后整体导致第二层缩放小于1(就是图片比fit center状态还小),重新修正缩放

if(outerScale * scalePost <1f) {

scalePost =1f/ outerScale;

}

//如果缩放修正不为1,说明进行了修正

if(scalePost !=1f) {

change =true;

}

//尝试根据缩放点进行缩放修正

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;

}

//只有有执行修正才执行动画

if(change) {

//计算结束矩阵

Matrix animEnd = MathUtils.matrixTake(mOuterMatrix);

animEnd.postScale(scalePost,scalePost,mLastMovePoint.x,mLastMovePoint.y);

animEnd.postTranslate(postX,postY);

//清理当前可能正在执行的动画

cancelAllAnimator();

//启动矩阵动画

mScaleAnimator=newScaleAnimator(mOuterMatrix,animEnd);

mScaleAnimator.start();

//清理临时变量

MathUtils.matrixGiven(animEnd);

}

//清理临时变量

MathUtils.rectFGiven(testBound);

MathUtils.matrixGiven(testMatrix);

MathUtils.matrixGiven(currentMatrix);

}

/**

*执行惯性动画

*

*动画在遇到不能移动就停止.

*动画速度衰减到很小就停止.

*

*其中参数速度单位为 像素/秒

*

*@paramvxx方向速度

*@paramvyy方向速度

*/

private voidfling(floatvx, floatvy) {

if(!isReady()) {

return;

}

//清理当前可能正在执行的动画

cancelAllAnimator();

//创建惯性动画

//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,当速度衰减到小于1时停止.

*当图片不能移动时,动画停止.

*/

private classFlingAnimatorextendsValueAnimatorimplementsValueAnimator.AnimatorUpdateListener {

/**

*速度向量

*/

private float[]mVector;

/**

*创建惯性动画

*

*参数单位为 像素/帧

*

*@paramvectorX速度向量

*@paramvectorY速度向量

*/

publicFlingAnimator(floatvectorX, floatvectorY) {

super();

setFloatValues(0,1f);

setDuration(1000000);

addUpdateListener(this);

mVector=new float[]{vectorX,vectorY};

}

@Override

public voidonAnimationUpdate(ValueAnimator animation) {

//移动图像并给出结果

booleanresult = 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 classScaleAnimatorextendsValueAnimatorimplementsValueAnimator.AnimatorUpdateListener {

/**

*开始矩阵

*/

private float[]mStart=new float[9];

/**

*结束矩阵

*/

private float[]mEnd=new float[9];

/**

*中间结果矩阵

*/

private float[]mResult=new float[9];

/**

*构建一个缩放动画

*

*从一个矩阵变换到另外一个矩阵

*

*@paramstart开始矩阵

*@paramend结束矩阵

*/

publicScaleAnimator(Matrix start,Matrix end) {

this(start,end,SCALE_ANIMATOR_DURATION);

}

/**

*构建一个缩放动画

*

*从一个矩阵变换到另外一个矩阵

*

*@paramstart开始矩阵

*@paramend结束矩阵

*@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) {

//获取动画进度

floatvalue = (Float) animation.getAnimatedValue();

//根据动画进度计算矩阵中间插值

for(inti =0;i <9;i++) {

mResult[i] =mStart[i] + (mEnd[i] -mStart[i]) * value;

}

//设置矩阵并重绘

mOuterMatrix.setValues(mResult);

dispatchOuterMatrixChanged();

invalidate();

}

}

////////////////////////////////防止内存抖动复用对象////////////////////////////////

/**

*对象池

*

*防止频繁new对象产生内存抖动.

*由于对象池最大长度限制,如果吞度量超过对象池容量,仍然会发生抖动.

*此时需要增大对象池容量,但是会占用更多内存.

*

*@param对象池容纳的对象类型

*/

private static abstract classObjectsPool {

/**

*对象池的最大容量

*/

private intmSize;

/**

*对象池队列

*/

privateQueuemQueue;

/**

*创建一个对象池

*

*@paramsize对象池最大容量

*/

publicObjectsPool(intsize) {

mSize= size;

mQueue=newLinkedList();

}

/**

*获取一个空闲的对象

*

*如果对象池为空,则对象池自己会new一个返回.

*如果对象池内有对象,则取一个已存在的返回.

* take出来的对象用完要记得调用given归还.

*如果不归还,让然会发生内存抖动,但不会引起泄漏.

*

*@return可用的对象

*

*@see#given(Object)

*/

publicTtake() {

//如果池内为空就创建一个

if(mQueue.size() ==0) {

returnnewInstance();

}else{

//对象池里有就从顶端拿出来一个返回

returnresetInstance(mQueue.poll());

}

}

/**

*归还对象池内申请的对象

*

*如果归还的对象数量超过对象池容量,那么归还的对象就会被丢弃.

*

*@paramobj归还的对象

*

*@see#take()

*/

public voidgiven(Tobj) {

//如果对象池还有空位子就归还对象

if(obj !=null&&mQueue.size()

mQueue.offer(obj);

}

}

/**

*实例化对象

*

*@return创建的对象

*/

abstract protectedTnewInstance();

/**

*重置对象

*

*把对象数据清空到就像刚创建的一样.

*

*@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;

}

}

////////////////////////////////数学计算工具类////////////////////////////////

/**

*数学计算工具类

*/

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就是结果

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图片应该在ImageView中展示的矩形

*/

public static voidcalculateScaledRectInContainer(RectF container, floatsrcWidth, floatsrcHeight,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);

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);

}

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容