Android手势检测 带你打造支持图片缩放、平移预览(下)

前面一片博客讲解和支持图片缩放的预览,但是那个缩放的缩放中心是固定的都是屏幕中心,这显然不能满足用户的需求,我们需要的是缩放焦点能够随着手势变化,还能够双击缩放,并且可以平移,本片博客就带大家来实现这些功能。这篇文章是基于 Android手势检测 带你打造图片缩放预览(上)的。


首先给出效果图:

贴出源代码,然后再对每个部分进行分析

class ScaleMoveImageViewer extends ImageView implements OnTouchListener,OnScaleGestureListener{
	private ScaleGestureDetector sgc;
	private GestureDetector gd;
	private float SOURCE_SCALE;
	private	Matrix matrix=new Matrix();
	private float[] values=new float[9];
	private boolean once=true;
	private float preX,preY,currentX,currentY;
	private int prePointerCount;
	
	private static final int REQUESTCODE_BIGER=1;
	private static final int REQUESTCODE_SMALLER=2;
	private static final float BIGER_TMP_SCALE=1.06f;
	private static final float SMALLER_TMP_SCALE=0.94f;
	private static final float MAX_SCALE=4.0F;
	private static final float MIN_SCALE=0.2F;
	
	public ScaleMoveImageViewer(Context context) {
		this(context,null);		
	}
	public ScaleMoveImageViewer(Context context, AttributeSet attrs) {
		super(context, attrs);
		super.setScaleType(ScaleType.MATRIX);  
		this.setOnTouchListener(this);
		sgc=new ScaleGestureDetector(context, this);
		gd=new GestureDetector(context, new SimpleOnGestureListener(){
			@Override
			public boolean onDoubleTap(MotionEvent e) {
				Log.i("TAG","onDoubleTap");
				float x=e.getX();
				float y=e.getY();
				setDobleTapScale(x, y);
                return true; 
			}
		});
		
	}

	//手指缩放
	@Override
	public boolean onScale(ScaleGestureDetector detector) {  
		float scaleFactor=detector.getScaleFactor();
		float currentScale=getScale();//相对原图的缩放比例
		if(currentScale>MAX_SCALE && scaleFactor<1.0f || currentScale<MIN_SCALE 
		&& scaleFactor>1.0f || currentScale<MAX_SCALE && currentScale>MIN_SCALE){
			matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
		}
		ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
		setImageMatrix(matrix);
		return true;
	}
	
	//移动
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		currentX=0;currentY=0;
		int pointerCount=event.getPointerCount();
		for(int i=0;i<pointerCount;i++){
			currentX+=event.getX();
			currentY+=event.getY();
		}
		currentX/=pointerCount;
		currentY/=pointerCount;
		if (pointerCount!=prePointerCount) {
			preX=currentX;
			preY=currentY;
			prePointerCount=pointerCount;
		}
		switch (event.getAction()) {
		case MotionEvent.ACTION_MOVE:
			float dx=currentX-preX;
			float dy=currentY-preY;
			ImagePositonManager.setMovePosition(getDrawable(), matrix, dx, dy, getWidth(), getHeight());
			setImageMatrix(matrix);
			preX=currentX;
			preY=currentY;
			break;
		case MotionEvent.ACTION_UP://有多根手指触摸屏幕时,只有当所有的手指抬起时这里才执行
			prePointerCount=0;
			break;
		}
		gd.onTouchEvent(event);
		return sgc.onTouchEvent(event);
	}
	//双击缩放
	public void setDobleTapScale(float px,float py){
		float currectScale=getScale();
		if(currectScale<SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(SOURCE_SCALE, px, py,REQUESTCODE_BIGER), 10);
		}
		if(currectScale==SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(MAX_SCALE-1, px, py,REQUESTCODE_BIGER), 10);
		}
		if(currectScale>SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(SOURCE_SCALE, px, py,REQUESTCODE_SMALLER), 10);
		}
		ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
		setImageMatrix(matrix);		
	}
	
	private class AutoScaleRunnable implements Runnable{
		
		float targetScale=0;
		float px=0;
		float py=0;
		int requestCode=0;
		public AutoScaleRunnable(float targetScale,float px,float py,int requestCode){
			this.targetScale=targetScale;
			this.px=px;
			this.py=py;
			this.requestCode=requestCode;
		}
		
		@Override
		public void run() {
			if(requestCode==REQUESTCODE_BIGER){
				matrix.postScale(BIGER_TMP_SCALE, BIGER_TMP_SCALE, px, py);
				ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
				setImageMatrix(matrix);
				float currentScale = getScale();
				if (currentScale<targetScale) {
					ScaleMoveImageViewer.this.postDelayed(this, 10);
				}else {
					while(getScale()!=targetScale){
						matrix.postScale(targetScale/getScale(), targetScale/getScale(), px, py);
						ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
						setImageMatrix(matrix);
					}
				}
			}
			else if (requestCode==REQUESTCODE_SMALLER) {
				matrix.postScale(SMALLER_TMP_SCALE, SMALLER_TMP_SCALE, px, py);
				ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
				setImageMatrix(matrix);
				float currentScale = getScale();
				if(currentScale>targetScale){
					ScaleMoveImageViewer.this.postDelayed(this, 10);
				}else {
					while(getScale()!=targetScale){
						matrix.postScale(targetScale/getScale(), targetScale/getScale(), px, py);
						ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
						setImageMatrix(matrix);
					}
				}
			}
		}
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		if(once){
			matrix=getImageMatrix();
			once=false;
			Drawable drawable=getDrawable();
			//获取图片的宽和高
			int dw=drawable.getIntrinsicWidth();
			int dh=drawable.getIntrinsicHeight();
			int w=getWidth();
			int h=getHeight();
			float scale=Math.min(1.0f*w/dw, 1.0f*h/dh);
			SOURCE_SCALE=scale;
			matrix.postTranslate(w/2-dw/2, h/2-dh/2);
			matrix.postScale(scale, scale, w/2, h/2);
			setImageMatrix(matrix);			
		}  
		super.onDraw(canvas);
	}
	
	private float getScale(){
		matrix.getValues(values);
		return values[Matrix.MSCALE_X];
	}
	
	@Override
	public boolean onScaleBegin(ScaleGestureDetector detector) {
		return true;
		
	}

	@Override
	public void onScaleEnd(ScaleGestureDetector detector) {
		
	}
}
第二部分工具类,来控制图片的位置
public class ImagePositonManager {

	//缩放图片时控制其显示位置
	public static void setShowPosition(Drawable drawable,Matrix matrix,int w,int h){
		RectF rectF=new RectF(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
		matrix.mapRect(rectF);
		float rw=rectF.width();
		float rh=rectF.height();
		float moveX=0,moveY=0;
		if(rw<=w){
			moveX=w/2-rw/2-rectF.left;
		}
		if (rh<=h) {
			moveY=h/2-rh/2-rectF.top;
		}
		if(rw>w && rectF.left>0){
			moveX=-rectF.left;
		}
		if(rw>w && rectF.right<w){
			moveX=w-rectF.right;
		}
		if(rh>h && rectF.top>0){
			moveY=-rectF.top;
		}
		if(rh>h && rectF.bottom<h){
			moveY=h-rectF.bottom;
		}
		matrix.postTranslate(moveX, moveY);
	}
	
	//移动图片时控制显示位置
	public static void setMovePosition(Drawable drawable,Matrix matrix,float dx,float dy,int w,int h){
		RectF rectF=new RectF(0,0,drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
		matrix.mapRect(rectF);
		float rw=rectF.width();
		float rh=rectF.height();
		if(rw>w && rectF.left+dx<=0 && rectF.right+dx>=w){
			matrix.postTranslate(dx, 0);
		}
		if(rh>h && rectF.top+dy<=0 && rectF.bottom+dy>=h){
			matrix.postTranslate(0, dy);
		}
	}
	
}


最后就是主函数和布局文件,这个很简单不贴代码了

现在对每个部分进行分析
//移动
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		currentX=0;currentY=0;
		int pointerCount=event.getPointerCount();
		for(int i=0;i<pointerCount;i++){
			currentX+=event.getX();
			currentY+=event.getY();
		}
		currentX/=pointerCount;
		currentY/=pointerCount;
		if (pointerCount!=prePointerCount) {
			preX=currentX;
			preY=currentY;
			prePointerCount=pointerCount;
		}
		switch (event.getAction()) {
		case MotionEvent.ACTION_MOVE:
			float dx=currentX-preX;
			float dy=currentY-preY;
			ImagePositonManager.setMovePosition(getDrawable(), matrix, dx, dy, getWidth(), getHeight());
			setImageMatrix(matrix);
			preX=currentX;
			preY=currentY;
			break;
		case MotionEvent.ACTION_UP://有多根手指触摸屏幕时,只有当所有的手指抬起时这里才执行
			prePointerCount=0;
			break;
		}
		gd.onTouchEvent(event);
		return sgc.onTouchEvent(event);
	}
关于图片的移动,代码挺简单相信大家都能看的懂,但是有几点需要注意的地方
1、图片的一定是一个多点操作,所以触摸点的位置我们去所有手指位置的平均值
2、要特别注意这段代码
if (pointerCount!=prePointerCount) {
preX=currentX;
preY=currentY;
prePointerCount=pointerCount;
}当触摸点的数量发生变化时,即有手指抬起或按下时,这是我们为了保证图片不移动,需要将前一次的x,y值赋值为当前的x,y值。如果不这样做,当我们的手放在屏幕上不动,r然后突然抬起(按下)一根手指,这是照片就会移动。

public void setDobleTapScale(float px,float py){
		float currectScale=getScale();
		if(currectScale<SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(SOURCE_SCALE, px, py,REQUESTCODE_BIGER), 10);
		}
		if(currectScale==SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(MAX_SCALE-1, px, py,REQUESTCODE_BIGER), 10);
		}
		if(currectScale>SOURCE_SCALE){
			ScaleMoveImageViewer.this.postDelayed(new AutoScaleRunnable(SOURCE_SCALE, px, py,REQUESTCODE_SMALLER), 10);
		}
		ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
		setImageMatrix(matrix);		
	}
	
	private class AutoScaleRunnable implements Runnable{
		
		float targetScale=0;
		float px=0;
		float py=0;
		int requestCode=0;
		public AutoScaleRunnable(float targetScale,float px,float py,int requestCode){
			this.targetScale=targetScale;
			this.px=px;
			this.py=py;
			this.requestCode=requestCode;
		}
		
		@Override
		public void run() {
			if(requestCode==REQUESTCODE_BIGER){
				matrix.postScale(BIGER_TMP_SCALE, BIGER_TMP_SCALE, px, py);
				ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
				setImageMatrix(matrix);
				float currentScale = getScale();
				if (currentScale<targetScale) {
					ScaleMoveImageViewer.this.postDelayed(this, 10);
				}else {
					while(getScale()!=targetScale){
						matrix.postScale(targetScale/getScale(), targetScale/getScale(), px, py);
						ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
						setImageMatrix(matrix);
					}
				}
			}
			else if (requestCode==REQUESTCODE_SMALLER) {
				matrix.postScale(SMALLER_TMP_SCALE, SMALLER_TMP_SCALE, px, py);
				ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
				setImageMatrix(matrix);
				float currentScale = getScale();
				if(currentScale>targetScale){
					ScaleMoveImageViewer.this.postDelayed(this, 10);
				}else {
					while(getScale()!=targetScale){
						matrix.postScale(targetScale/getScale(), targetScale/getScale(), px, py);
						ImagePositonManager.setShowPosition(getDrawable(), matrix, getWidth(), getHeight());
						setImageMatrix(matrix);
					}
				}
			}
		}
	}
双击缩放,不能突然一下子就变大或变小,这样用户体验效果很差,所以我们做成了一个动画效果,这里我们主要来讲解AutoScaleRunnable,当用户双击图片后,
AutoScaleRunnable 会执行,注意我们matrix.postScale(BIGER_TMP_SCALE, BIGER_TMP_SCALE, px, py);BIGER_TMP_SCALE=是一个接近1  比1大一点的数,所以我们每一次都是只缩放一点点,通过反复的执行run方法就达到了缩放到最终目标大小targetScale,如何反复呢,我们通过这句代码来实现
if (currentScale<targetScale) {
ScaleMoveImageViewer.this.postDelayed(this, 10);
}

代码分析到这里已经结束了!








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值