Android 在viewPager中双指缩放图片双击缩放图片单指拖拽图片的实现思路
我们就把这个问题叫做图片查看器吧,它的主要功能有:
1、双击缩放图片。
2、双指缩放图片。
3、单指拖拽图片。
为此这个图片查看器需要考虑以下的技术点:
一、双击缩放图片:
1、如果图片高度比屏幕的高度小得多,那么就将图片放大到高度与屏幕高度相等,否则就放大一个特定的倍数。
2、如何判断是否到达这个倍数来停止缩放。
3、判断完且停止放大后,图片可能已经超出了这个倍数需要的大小,如何回归到我们的目标大小。
4、判断完且停止缩小后,图片宽度可能已经小于屏幕宽度,在两边留下了空白,如何重置为原来的大小。
二、双指缩放图片:
1、双指缩放,放大一个特定的倍数停止。
2、如何判断是否到达这个倍数。
3、放大停止后,图片可能已经超出了这个倍数需要的大小,如何回归到我们的目标大小。
4、缩小停止后,图片宽度可能已经小于屏幕宽度,在两边留下了空白,如何重置为原来的大小。
三、单指拖拽:
1、当图片宽度小于或等于屏幕宽度的时候,禁止左右移动,当图片的高度小于屏幕高度的时候,禁止上下移动。
2、移动图片时,如果图片的一边已经与屏幕之间有了空白,松手后恢复,让图片的这一边与屏幕边界重合。
四、
如何判断是双击,还是多指触控,还是单指。
五、
如何解决与viewPager的滑动冲突,当图片已经滑动到尽头无法滑动时,此时viewPager应该拦截事件。
我们逐一来解决:
publicclassMyImageViewextendsImageViewimplementsViewTreeObserver.OnGlobalLayoutListener,View.OnTouchListener{
publicMyImageView(Contextcontext,AttributeSetattrs){
super(context,attrs);
super.setScaleType(ScaleType.MATRIX);
setOnTouchListener(this);
/**
*双击实现图片放大缩小
*/
mGestureDetector=newGestureDetector(context,
newGestureDetector.SimpleOnGestureListener(){
@Override
publicbooleanonDoubleTap(MotionEvente){
changeViewSize(e);
returntrue;
}
});
}
在这里缩放图片是用matrix,因此首先要设置scaleType为matrix。
用手势判断双击行为。不要忘了在onTouch里面加上
if(mGestureDetector.onTouchEvent(event)) returntrue;
判断单指与多指触控,则在onTouch里面判断,要用event.getAction()&MotionEvent.ACTION_MASK来判断。
//多指触控模式,单指,双指
privateintmode;
privatefinalstaticintSINGLE_TOUCH=1;//单指
privatefinalstaticintDOUBLE_TOUCH=2;//双指
@Override
publicbooleanonTouch(Viewview,MotionEventevent){
rectF=getMatrixRectF();
if(mGestureDetector.onTouchEvent(event))
returntrue;
switch(event.getAction()&event.getActionMasked()){
caseMotionEvent.ACTION_DOWN:
mode=SINGLE_TOUCH;
break;
caseMotionEvent.ACTION_MOVE:
if(mode>=DOUBLE_TOUCH)//双指缩放
{
}
if(mode==SINGLE_TOUCH)//单指拖拽
{
}
break;
caseMotionEvent.ACTION_POINTER_DOWN:
mode+=1;break;
caseMotionEvent.ACTION_POINTER_UP:
mode-=1;
break;
caseMotionEvent.ACTION_UP:
mode=0;
break;
//在ACTION_MOVE中,事件被拦截了之后,有时候ACTION_UP无法触发,所以加上了ACTION_CANCEL
caseMotionEvent.ACTION_CANCEL:
mode=0;
break;
default:
break;
}
returntrue;
}
有如下事件使我们要用到的:
MotionEvent.ACTION_DOWN:在第一个点被按下时触发
MotionEvent.ACTION_UP:当屏幕上唯一的点被放开时触发
MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有一个点被按住,此时再按下其他点时触发。
MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)。
MotionEvent.ACTION_MOVE:当有点在屏幕上移动时触发。值得注意的是,由于它的灵敏度很高,而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖动),所以实际的情况是,基本上只要有点在屏幕上,此事件就会一直不停地被触发。
在ACTION_MOVE中通过mode的大小来判断是单指还是双指。
不过有一个令人伤心的事情,Android自己有一个bug。经过测试发现双指交换触碰图片的时候,程序会闪退,出现异常:pointIndexoutofrange。这是Android自己的bug。个人觉得最好得解决方法是自定义一个viewPager,然后在里面重写:onTouchEvent,onInterceptTouchEvent,然后捕获异常。
@Override
publicbooleanonTouchEvent(MotionEventev){
try{
returnsuper.onTouchEvent(ev);
}catch(IllegalArgumentExceptionex){
ex.printStackTrace();
}
returnfalse;
}
@Override
publicbooleanonInterceptTouchEvent(MotionEventev){
try{
returnsuper.onInterceptTouchEvent(ev);
}catch(IllegalArgumentExceptionex){
ex.printStackTrace();
}
returnfalse;
}
这样程序就不会闪退了。
我们来看看双击放大的的代码:
/**
*双击缩放图片
*/
privatevoidchangeViewSize(MotionEvente){
//获取双击的坐标
finalfloatx=e.getX();
finalfloaty=e.getY();
//如果此时还在缩放那就直接返回
if(animator!=null&&animator.isRunning())
return;
//判断是处于放大还是缩小的状态
if(!isZoomChanged()){
animator=ValueAnimator.ofFloat(1.0f,2.0f);
}else{
animator=ValueAnimator.ofFloat(1.0f,0.0f);
}
animator.setTarget(this);
animator.setDuration(500);
animator.setInterpolator(newDecelerateInterpolator());
animator.start();
animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){
@Override
publicvoidonAnimationUpdate(ValueAnimatorvalueAnimator){
Floatvalue=(Float)animator.getAnimatedValue();
matrix.postScale(value,value,x,y);
checkBorderAndCenterWhenScale();//在缩放后让图片居中
setImageMatrix(matrix);
/**
*控制缩小的范围
*如果已经小于初始大小,那么恢复到初始大小,然后停止
*/
if(checkRestScale()){
matrix.set(oldMatrix);//oldMatrix为最原始的matrix
setImageMatrix(matrix);
return;
}
/**
*控制放大的范围
*如果已经大于目标的放大倍数,那么直接置为目标的放大倍数
*然后停止
*/
if(getMatrixValueX()>=mDoubleClickScale)
{
matrix.postScale(mDoubleClickScale/getMatrixValueX(),mDoubleClickScale/getMatrixValueX(),x,y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
return;
}
}
});
}
判断处于放大还是缩小状态的代码:(不是初始值就说明是处于放大状态)
/**
*判断缩放级别是否是改变过
*
*@returntrue表示非初始值,false表示初始值
*/
privatebooleanisZoomChanged(){
float[]values=newfloat[9];
getImageMatrix().getValues(values);
//获取当前X轴缩放级别
floatscale=values[Matrix.MSCALE_X];
//获取初始时候的X轴缩放级别,两者做比较
oldMatrix.getValues(values);
returnscale!=values[Matrix.MSCALE_X];
}
getMatrixValue()的代码如下,是为了取得当前的放大倍数,相对于一开始的图片来说
privatefloatgetMatrixValueX()
{
//TODOAuto-generatedmethodstub
float[]values=newfloat[9];
getImageMatrix().getValues(values);
//获取当前X轴缩放级别
floatscale=values[Matrix.MSCALE_X];
//获取原始Matrix的X轴缩放级别
oldMatrix.getValues(values);
//返回放大的倍数
returnscale/values[Matrix.MSCALE_X];
}
checkRestScale()的代码如下,主要是为了判断当前的缩放级别是否小于最初始的缩放级别。
/**
*判断是否需要重置
*
*@return当前缩放级别小于原始缩放级别时,重置
*/
privatebooleancheckRestScale(){
//TODOAuto-generatedmethodstub
float[]values=newfloat[9];
getImageMatrix().getValues(values);
//获取当前X轴缩放级别
floatscale=values[Matrix.MSCALE_X];
//获取原始的X轴缩放级别,两者做比较
oldMatrix.getValues(values);
returnscale
checkBorderAndCenterWhenScale()的代码如下,否则图片缩放后位置会发生变化。
/**
*在缩放时,进行图片显示范围的控制
*/
privatevoidcheckBorderAndCenterWhenScale()
{
RectFrect=getMatrixRectF();
floatdeltaX=0;
floatdeltaY=0;
intwidth=getWidth();
intheight=getHeight();
//如果宽或高大于屏幕,则控制范围
if(rect.width()>=width)
{
if(rect.left>0)
{
deltaX=-rect.left;
}
if(rect.right=height)
{
if(rect.top>0)
{
deltaY=-rect.top;
}
if(rect.bottom
接下看看双指缩放和单指拖拽:
@Override
publicbooleanonTouch(Viewview,MotionEventevent){
rectF=getMatrixRectF();//获取图片边界范围
if(mGestureDetector.onTouchEvent(event))
returntrue;
switch(event.getAction()&event.getActionMasked()){
caseMotionEvent.ACTION_DOWN:
//如果放大后图片的边界超出了屏幕,那么就拦截事件,不让viewPager处理
if(rectF.width()>getWidth()||rectF.height()>getHeight()){
getParent().requestDisallowInterceptTouchEvent(true);
}
mode=SINGLE_TOUCH;
x=(int)event.getRawX();
y=(int)event.getRawY();
break;
caseMotionEvent.ACTION_MOVE:
if(mode>=DOUBLE_TOUCH)//双指缩放
{
getParent().requestDisallowInterceptTouchEvent(true);
newDist=calculateDist(event);//计算距离
Pointpoint=getMiPoint(event);//获取两手指间的中点坐标
if(newDist>oldDist+1)//放大(加一是为了防止抖动)
{
changeViewSize(oldDist,newDist,point);//根据距离实现放大缩小
oldDist=newDist;
}
if(oldDist>newDist+1)//缩小
{
changeViewSize(oldDist,newDist,point);
oldDist=newDist;
}
}
if(mode==SINGLE_TOUCH)//单指拖拽
{
floatdx=event.getRawX()-x;
floatdy=event.getRawY()-y;
//如果移动过程中图片的边界超出了屏幕,那么就拦截事件,不让viewPager处理
if(rectF.width()>getWidth()||rectF.height()>getHeight()){
getParent().requestDisallowInterceptTouchEvent(true);
}
//如果向右移动图片到了尽头,那么就不要拦截事件,让viewPager处理
if(rectF.left>=0&&dx>0)
getParent().requestDisallowInterceptTouchEvent(false);
//如果向左移动到了尽头,那么就不要拦截事件,让viewPager处理
if(rectF.right<=getWidth()&&dx<0)
getParent().requestDisallowInterceptTouchEvent(false);
if(getDrawable()!=null){
//如果图片宽度或高度没有超出屏幕,那么就禁止左右或上下滑动
if(rectF.width()<=getWidth())
dx=0;
if(rectF.height()=0&&dy>0)
dy=0;
//如果图片向上移动到了尽头,不让它继续移动
if(rectF.bottom<=getHeight()&&dy<0)
dy=0;
//当移动距离大于1的时候再移动,因为ACTION_MOVE比较灵敏,
//手指即使只是放在上面,依然能够检测到手指的抖动,然后让图片移动。
if(Math.abs(dx)>1||Math.abs(dy)>1)
matrix.postTranslate(dx,dy);
setImageMatrix(matrix);
}
}
x=(int)event.getRawX();
y=(int)event.getRawY();
break;
caseMotionEvent.ACTION_POINTER_DOWN:
mode+=1;
oldDist=calculateDist(event);break;
caseMotionEvent.ACTION_POINTER_UP:
mode-=1;
break;
caseMotionEvent.ACTION_UP:
backToPosition();
mode=0;
break;
//在ACTION_MOVE中,事件被拦截了之后,有时候ACTION_UP无法触发,所以加上了ACTION_CANCEL
caseMotionEvent.ACTION_CANCEL:
backToPosition();
mode=0;
break;
default:
break;
}
returntrue;
}
首先先来看一个方法,根据图片的matrix获得图片的边界范围,这个范围映射在rect上。(这个范围检测是用在单指拖拽上的)
/**
*根据当前图片的Matrix获得图片的范围
*
*@return
*/
privateRectFgetMatrixRectF()
{
RectFrect=newRectF();
Drawabled=getDrawable();
if(null!=d)
{
rect.set(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
matrix.mapRect(rect);
}
Log.e("aaaa",""+rect.bottom+""+rect.left+""+rect.right+""+rect.top);
returnrect;
}
rect.bottom:图片下边界的纵坐标
rect.left:图片左边界的横坐标
rect.right:图片右边界的横坐标
rect.top:图片上边界的纵坐标
rectF.width():图片宽度
rectF.height():图片高度
需要注意的是Matrix对图片的操作都是操作ImageView里面的bitmap,ImageView是没有变化的,上面所说的屏幕边界其实ImageView的边界,getWidth(),getHeight()是ImageView的宽和高。
方法backToPosition()主要是实现单指拖拽的技术点2,当手指快速划过去的时候,在检测到无法继续滑动前图片边界与屏幕边界已经出现了距离,所以松开手指的时候要复位,让图片边界与屏幕边界重合。
/**
*若是在移动后图片的边界脱离屏幕边界,那么就让图片边界与屏幕边界重合
*若手指快速移动,停止后会出现图片距离屏幕有一段空白距离,然后经过判断不能再移动,
*但是在进行下一次判断是否可以继续移动之前就已经出现了。
*所以需要复位
*/
privatevoidbackToPosition(){
if(rectF.left>=0){//图片左边界与屏幕出现距离
matrix.postTranslate(-rectF.left,0);
setImageMatrix(matrix);
}
if(rectF.right<=getWidth()){//图片右边界与屏幕出现距离
matrix.postTranslate(getWidth()-rectF.right,0);
setImageMatrix(matrix);
}
if(rectF.top>=0){//图片上边界与屏幕出现距离
matrix.postTranslate(0,-rectF.top);
setImageMatrix(matrix);
}
if(rectF.bottom<=getHeight()){//图片下边界与屏幕出现距离
matrix.postTranslate(0,getHeight()-rectF.bottom);
setImageMatrix(matrix);
}
}
获取两手指间的中点坐标
/**
*获取双指缩放时候的缩放中点
*
*@return
*/
privatePointgetMiPoint(MotionEventevent){
floatx=event.getX(0)+event.getX(1);
floaty=event.getY(0)+event.getY(1);
mPoint.set((int)x/2,(int)y/2);
returnmPoint;
}
计算两指触摸点的距离
/**
*计算两指触摸点之间的距离
*/
privatefloatcalculateDist(MotionEventevent){
floatx=event.getX(0)-event.getX(1);
floaty=event.getY(0)-event.getY(1);
return(float)Math.sqrt(x*x+y*y);
}
双指缩放图片
/**
*双指缩放图片
*/
privatevoidchangeViewSize(floatoldDist,floatnewDist,PointmPoint){
floatscale=newDist/oldDist;//缩放比例
matrix.postScale(scale,scale,mPoint.x,mPoint.y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
//防止缩小的时候小于初始的图片大小,需要重置
reSetMatrix();
//如果缩放已经大于目标倍数,停止,因为有可能已经超出,那么就直接缩放到目标大小
if(getMatrixValueX()>=MAX_SCALE)
{
matrix.postScale(MAX_SCALE/getMatrixValueX(),MAX_SCALE/getMatrixValueX(),x,y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
return;
}
}
reSetMatrix()的代码如下:
/**
*重置Matrix
*/
privatevoidreSetMatrix(){
if(checkRestScale()){
matrix.set(oldMatrix);
setImageMatrix(matrix);
return;
}
}
checkRestScale()的代码在上面已经给出了。oldMatrix为最初始的Matrix。
到这里还没有结束,设置Imageview的ScaleType为Matrix,那么图片不会主动缩放到适应屏幕,也不会处于屏幕中间,因此我们的自定义ImageView需要继承ViewTreeObserver.OnGlobalLayoutListener
@Override
protectedvoidonAttachedToWindow(){
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
publicvoidonGlobalLayout(){
if(once)
{
Drawabled=getDrawable();
if(d==null)
return;
Log.e("TAG",d.getIntrinsicWidth()+","+d.getIntrinsicHeight());
intwidth=getWidth();
intheight=getHeight();
//拿到图片的宽和高
intdw=d.getIntrinsicWidth();
intdh=d.getIntrinsicHeight();
floatscale=1.0f;
//如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
if(dw>width&&dh<=height)
{
scale=width*1.0f/dw;
}
if(dh>height&&dw<=width)
{
scale=height*1.0f/dh;
}
//如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
if(dw>width&&dh>height)
{
scale=Math.min(width*1.0f/dw,height*1.0f/dh);
}
initScale=scale;
Log.e("TAG","initScale="+initScale);
matrix.postTranslate((width-dw)/2,(height-dh)/2);
matrix.postScale(scale,scale,getWidth()/2,
getHeight()/2);
//图片移动至屏幕中心
setImageMatrix(matrix);
oldMatrix.set(getImageMatrix());
once=false;
RectFrectF=getMatrixRectF();
setDoubleClickScale(rectF);
}
}
//拿到图片的宽和高
intdw=d.getIntrinsicWidth();
intdh=d.getIntrinsicHeight();
拿到的图片宽和高是bitmap的真实高度。
初始的oldMatrix就是在这里设置的,然后作为初始模板,代表着图片没被动手改变的Matrix。至于方法setDoubleClickScale(rectF);只是设置双击放大的倍数而已,如果图片高度比屏幕的高度小得多,那么就将图片放大到高度与屏幕高度相等,否则就放大一个特定的倍数。必须在这里设置,因为在这里取到的rectF才能反映原始图片的边界,因为这时候还没有动手改变图片。
/**
*设置双击放大的倍数
*/
privatevoidsetDoubleClickScale(RectFrectF)
{
if(rectF.height()
到这里大概结束了,下面就贴出完整的代码:
packagecom.example.tangzh.myimageview;
importandroid.animation.ValueAnimator;
importandroid.content.Context;
importandroid.graphics.Matrix;
importandroid.graphics.Point;
importandroid.graphics.RectF;
importandroid.graphics.drawable.Drawable;
importandroid.util.AttributeSet;
importandroid.util.Log;
importandroid.view.GestureDetector;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewTreeObserver;
importandroid.view.animation.DecelerateInterpolator;
importandroid.widget.ImageView;
/**
*CreatedbyTangZHon2017/5/3.
*/
publicclassMyImageViewextendsImageViewimplementsViewTreeObserver.OnGlobalLayoutListener,View.OnTouchListener{
privatefinalstaticintSINGLE_TOUCH=1;//单指
privatefinalstaticintDOUBLE_TOUCH=2;//双指
//多指触控模式,单指,双指
privateintmode;
//两指触碰点之间的距离
privatefloatoldDist;
privatefloatnewDist;
/**
*最大缩放级别
*/
privatestaticfinalfloatMAX_SCALE=5f;
/**
*双击时的缩放级别
*/
privatefloatmDoubleClickScale=2;
/**
*初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0
*/
privatefloatinitScale=1.0f;
privatebooleanonce=true;
privateRectFrectF;
/**
*用于双击检测
*/
privateGestureDetectormGestureDetector;
privateintx=0;
privateinty=0;
privatePointmPoint=newPoint();
privatefinalMatrixmatrix=newMatrix();
privateMatrixoldMatrix=newMatrix();
privateValueAnimatoranimator;
publicMyImageView(Contextcontext){
this(context,null);
}
publicMyImageView(Contextcontext,AttributeSetattrs){
super(context,attrs);
super.setScaleType(ScaleType.MATRIX);
setOnTouchListener(this);
/**
*双击实现图片放大缩小
*/
mGestureDetector=newGestureDetector(context,
newGestureDetector.SimpleOnGestureListener(){
@Override
publicbooleanonDoubleTap(MotionEvente){
changeViewSize(e);
returntrue;
}
});
}
@Override
publicbooleanonTouch(Viewview,MotionEventevent){
rectF=getMatrixRectF();//获取图片边界范围
if(mGestureDetector.onTouchEvent(event))
returntrue;
switch(event.getAction()&event.getActionMasked()){
caseMotionEvent.ACTION_DOWN:
//如果放大后图片的边界超出了屏幕,那么就拦截事件,不让viewPager处理
if(rectF.width()>getWidth()||rectF.height()>getHeight()){
getParent().requestDisallowInterceptTouchEvent(true);
}
mode=SINGLE_TOUCH;
x=(int)event.getRawX();
y=(int)event.getRawY();
break;
caseMotionEvent.ACTION_MOVE:
if(mode>=DOUBLE_TOUCH)//双指缩放
{
getParent().requestDisallowInterceptTouchEvent(true);
newDist=calculateDist(event);//计算距离
Pointpoint=getMiPoint(event);//获取两手指间的中点坐标
if(newDist>oldDist+1)//放大(加一是为了防止抖动)
{
changeViewSize(oldDist,newDist,point);//根据距离实现放大缩小
oldDist=newDist;
}
if(oldDist>newDist+1)//缩小
{
changeViewSize(oldDist,newDist,point);
oldDist=newDist;
}
}
if(mode==SINGLE_TOUCH)//单指拖拽
{
floatdx=event.getRawX()-x;
floatdy=event.getRawY()-y;
//如果移动过程中图片的边界超出了屏幕,那么就拦截事件,不让viewPager处理
if(rectF.width()>getWidth()||rectF.height()>getHeight()){
getParent().requestDisallowInterceptTouchEvent(true);
}
//如果向右移动图片到了尽头,那么就不要拦截事件,让viewPager处理
if(rectF.left>=0&&dx>0)
getParent().requestDisallowInterceptTouchEvent(false);
//如果向左移动到了尽头,那么就不要拦截事件,让viewPager处理
if(rectF.right<=getWidth()&&dx<0)
getParent().requestDisallowInterceptTouchEvent(false);
if(getDrawable()!=null){
//如果图片宽度或高度没有超出屏幕,那么就禁止左右或上下滑动
if(rectF.width()<=getWidth())
dx=0;
if(rectF.height()=0&&dy>0)
dy=0;
//如果图片向上移动到了尽头,不让它继续移动
if(rectF.bottom<=getHeight()&&dy<0)
dy=0;
//当移动距离大于1的时候再移动,因为ACTION_MOVE比较灵敏,
//手指即使只是放在上面,依然能够检测到手指的抖动,然后让图片移动。
if(Math.abs(dx)>1||Math.abs(dy)>1)
matrix.postTranslate(dx,dy);
setImageMatrix(matrix);
}
}
x=(int)event.getRawX();
y=(int)event.getRawY();
break;
caseMotionEvent.ACTION_POINTER_DOWN:
mode+=1;
oldDist=calculateDist(event);
Log.e("q",""+"a");
Log.e(":::",""+event.getPointerCount()+""+event.getActionIndex()+""+event.findPointerIndex(0));
break;
caseMotionEvent.ACTION_POINTER_UP:
mode-=1;
break;
caseMotionEvent.ACTION_UP:
backToPosition();
mode=0;
break;
//在ACTION_MOVE中,事件被拦截了之后,有时候ACTION_UP无法触发,所以加上了ACTION_CANCEL
caseMotionEvent.ACTION_CANCEL:
backToPosition();
mode=0;
break;
default:
break;
}
returntrue;
}
/**
*计算两指触摸点之间的距离
*/
privatefloatcalculateDist(MotionEventevent){
floatx=event.getX(0)-event.getX(1);
floaty=event.getY(0)-event.getY(1);
return(float)Math.sqrt(x*x+y*y);
}
@Override
protectedvoidonAttachedToWindow(){
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
}
/**
*若是在移动后图片的边界脱离屏幕边界,那么就让图片边界与屏幕边界重合
*若手指快速移动,停止后会出现图片距离屏幕有一段空白距离,然后经过判断不能再移动,
*但是在进行下一次判断是否可以继续移动之前就已经出现了。
*所以需要复位
*/
privatevoidbackToPosition(){
if(rectF.left>=0){//图片左边界与屏幕出现距离
matrix.postTranslate(-rectF.left,0);
setImageMatrix(matrix);
}
if(rectF.right<=getWidth()){//图片右边界与屏幕出现距离
matrix.postTranslate(getWidth()-rectF.right,0);
setImageMatrix(matrix);
}
if(rectF.top>=0){//图片上边界与屏幕出现距离
matrix.postTranslate(0,-rectF.top);
setImageMatrix(matrix);
}
if(rectF.bottom<=getHeight()){//图片下边界与屏幕出现距离
matrix.postTranslate(0,getHeight()-rectF.bottom);
setImageMatrix(matrix);
}
}
/**
*获取双指缩放时候的缩放中点
*
*@return
*/
privatePointgetMiPoint(MotionEventevent){
floatx=event.getX(0)+event.getX(1);
floaty=event.getY(0)+event.getY(1);
mPoint.set((int)x/2,(int)y/2);
returnmPoint;
}
/**
*双指缩放图片
*/
privatevoidchangeViewSize(floatoldDist,floatnewDist,PointmPoint){
floatscale=newDist/oldDist;//缩放比例
matrix.postScale(scale,scale,mPoint.x,mPoint.y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
//防止缩小的时候小于初始的图片大小,需要重置
reSetMatrix();
//如果缩放已经大于目标倍数,停止,因为有可能已经超出,那么就直接缩放到目标大小
if(getMatrixValueX()>=MAX_SCALE)
{
matrix.postScale(MAX_SCALE/getMatrixValueX(),MAX_SCALE/getMatrixValueX(),x,y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
return;
}
}
/**
*双击缩放图片
*/
privatevoidchangeViewSize(MotionEvente){
//获取双击的坐标
finalfloatx=e.getX();
finalfloaty=e.getY();
//如果此时还在缩放那就直接返回
if(animator!=null&&animator.isRunning())
return;
//判断是处于放大还是缩小的状态
if(!isZoomChanged()){
animator=ValueAnimator.ofFloat(1.0f,2.0f);
}else{
animator=ValueAnimator.ofFloat(1.0f,0.0f);
}
animator.setTarget(this);
animator.setDuration(500);
animator.setInterpolator(newDecelerateInterpolator());
animator.start();
animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){
@Override
publicvoidonAnimationUpdate(ValueAnimatorvalueAnimator){
Floatvalue=(Float)animator.getAnimatedValue();
matrix.postScale(value,value,x,y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
/**
*控制缩小的范围
*如果已经小于初始大小,那么恢复到初始大小,然后停止
*/
if(checkRestScale()){
matrix.set(oldMatrix);
setImageMatrix(matrix);
return;
}
/**
*控制放大的范围
*如果已经大于目标的放大倍数,那么直接置为目标的放大倍数
*然后停止
*/
if(getMatrixValueX()>=mDoubleClickScale)
{
matrix.postScale(mDoubleClickScale/getMatrixValueX(),mDoubleClickScale/getMatrixValueX(),x,y);
checkBorderAndCenterWhenScale();
setImageMatrix(matrix);
return;
}
}
});
}
/**
*判断缩放级别是否是改变过
*
*@returntrue表示非初始值,false表示初始值
*/
privatebooleanisZoomChanged(){
float[]values=newfloat[9];
getImageMatrix().getValues(values);
//获取当前X轴缩放级别
floatscale=values[Matrix.MSCALE_X];
//获取模板的X轴缩放级别,两者做比较
oldMatrix.getValues(values);
returnscale!=values[Matrix.MSCALE_X];
}
/**
*重置Matrix
*/
privatevoidreSetMatrix(){
if(checkRestScale()){
matrix.set(oldMatrix);
setImageMatrix(matrix);
return;
}
}
/**
*设置双击放大的倍数
*/
privatevoidsetDoubleClickScale(RectFrectF)
{
if(rectF.height()=width)
{
if(rect.left>0)
{
deltaX=-rect.left;
}
if(rect.right=height)
{
if(rect.top>0)
{
deltaY=-rect.top;
}
if(rect.bottomwidth&&dh<=height)
{
scale=width*1.0f/dw;
}
if(dh>height&&dw<=width)
{
scale=height*1.0f/dh;
}
//如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
if(dw>width&&dh>height)
{
scale=Math.min(width*1.0f/dw,height*1.0f/dh);
}
initScale=scale;
Log.e("TAG","initScale="+initScale);
matrix.postTranslate((width-dw)/2,(height-dh)/2);
matrix.postScale(scale,scale,getWidth()/2,
getHeight()/2);
//图片移动至屏幕中心
setImageMatrix(matrix);
oldMatrix.set(getImageMatrix());
once=false;
RectFrectF=getMatrixRectF();
setDoubleClickScale(rectF);
}
}
}
唉,虽然已经写完了,但是还有一个问题没有解决,就是移动图片到尽头,这时候不要放手,往反方向移动,就会出现一个问题,图片反方向的部分被遮挡,无法看到,然后移动的时候是直接切换图片,而不再继续移动图片,这个问题的原因是:当你移动图片到尽头时,就把事件交给viewpager来处理了,即使再往反方向移动图片,viewPager也一样继续拦截了事件。目前没解决。
以上所述是小编给大家介绍的在viewPager中双指缩放图片双击缩放图片单指拖拽图片的实现思路,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!