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

来源:互联网 发布:新歌2016网络红歌情歌 编辑:程序博客网 时间:2024/06/08 11:04
前面一片博客讲解和支持图片缩放的预览,但是那个缩放的缩放中心是固定的都是屏幕中心,这显然不能满足用户的需求,我们需要的是缩放焦点能够随着手势变化,还能够双击缩放,并且可以平移,本片博客就带大家来实现这些功能。这篇文章是基于Android手势检测 带你打造图片缩放预览(上)的。


首先给出效果图:

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

class ScaleMoveImageViewer extends ImageView implements OnTouchListener,OnScaleGestureListener{private ScaleGestureDetector sgc;private GestureDetector gd;private float SOURCE_SCALE;privateMatrix 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(){@Overridepublic boolean onDoubleTap(MotionEvent e) {Log.i("TAG","onDoubleTap");float x=e.getX();float y=e.getY();setDobleTapScale(x, y);                return true; }});}//手指缩放@Overridepublic 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;}//移动@Overridepublic 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;}@Overridepublic 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);}}}}}@Overrideprotected 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];}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;}@Overridepublic 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);}}}


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

现在对每个部分进行分析
//移动@Overridepublic 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;}@Overridepublic 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
原创粉丝点击