本文会使用到的类 : PointF ,Matrix 以及 MutionEvent 。
下面就来介绍一下这几个类在本文中的作用
PointF :
顾名思义 Point就是点的意思 ,F则是Float ,所以这个类实现了一个以Float为单位的一个点的功能。
参数为x和y轴的坐标。
实现方法:PointF point = new PointF(0.2f,0.2f);
Matrix:
这个类表示的是一个3x3的矩阵,本文中不详细说明。
Matrix是实现了平行和旋转还有缩放功能的一个类,这里是用的是graphis包中的Matrix,
本文中会用到该类的两个方法
postTranslate(x,y);
postTranslate方法是平移,里面的参数x为横向平移距离,y为纵向平移距离。
postScale(scale,scale,mX,mY)
postScale方法是缩放,这里的scale指的是缩放倍数,这里xy都缩放scale倍,而后两个参数则是缩放的中心点。
MutionEvent:
mutionEvent则是所有触点的集合.他的对象是一个数组;我们可以通过get来获取到触点的信息。
以及一些动作常量。
ACTION.POINTER.DOWN:有pointer的为副触点 该变量指定的为两个或者两个以上的触点摁下的动作
ACTION.DOWN: 为单一触点摁下的动作
ACTION.MOVE:为 移动动作
等等 等下代码里可以看出来其他的用途
介绍完本文要用的类,来看一下怎么实现图片的平移和缩放的思路
1.继承ImageView并复写View.onTouchListener接口
2.在构造方法里触发onTouchListener的点击事件
3.在onTouch里面 对v.getAction进行判断 一共分为一下几种情况:
单指点下,单指松开,多指摁下,多指松开,进行不同的事件处理。
单指摁下,将触发模式设为拖拽模式,这种模式下,可以对图片进行拖拽 并对替换用的Matrix进行初始化,以及对恩下的点(起始点)进行初始化,代码如下
case MotionEvent.ACTION_DOWN: mode = MODE_DRAWE; currentMatrix.set(getImageMatrix()); firstPoint.set(event.getX(),event.getY()); break;
多指摁下,将触发模式设为缩放模式,这种模式下,可以对图片进行缩放, 我们对两点之间的距离进行计算(使用勾股定理)
如果大于10f 就进行缩放处理 并且初始化中间点(两点的横纵坐标相加除2)
case MotionEvent.ACTION_POINTER_DOWN: mode = MODE_ZOOM; overDistance = distance(event); if(overDistance > 10f){ mindPoint = getMidPoint(event); currentMatrix.set(getImageMatrix()); } break;
单指松开和多指松开的思路是一样的,都是讲触发模式设为离开模式 就可以了
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = MODE_LEAVE; break;
最后到了我们的重头戏,移动 移动要实现的逻辑分为主要两部。
1.判断触发模式(1.拖拽模式,2.缩放模式)
2.为各个模式设置事件
拖拽模式:1.计算拖拽的横纵长度(当前触点的坐标-原来的坐标),给图片的Matrix设置上替换的Matrix 2.使用拖拽方法。
缩放模式:1.计算拖拽的横纵长度,计算出两个触点之间的长度(也就是两个手之间的长度)2.如果两个触点间的长度大于10f视为缩放了,我们这里就要求出缩放的倍数(两个触点拖拽后的长度/原来两个触点的长度),然后使用缩放方法来进行缩放。
代码如下:
case MotionEvent.ACTION_MOVE: if(mode == MODE_DRAWE){ float dx = event.getX() - firstPoint.x; float dy = event.getY() - firstPoint.y; mImageMatrix.set(currentMatrix); mImageMatrix.postTranslate(dx,dy); }else if(mode == MODE_ZOOM){ float dx = event.getX() - firstPoint.x; float dy = event.getY() - firstPoint.y; float distance1 = distance(event); if(distance1 > 10f){ float scale = distance1 / overDistance; mImageMatrix.set(currentMatrix); mImageMatrix.postScale(scale,scale,dx,dy); } } break;
思路就是这些,下面放代码。希望大家可以慢慢琢磨,这个功能其实很容易,按照上面的思路一步步撸下来,就懂了!
public class NixoImageView2 extends android.support.v7.widget.AppCompatImageView implements View.OnTouchListener{ private float mode = 0; private static final float MODE_DRAWE = 1; private static final float MODE_ZOOM = 2; private static final float MODE_LEAVE = 0; private PointF firstPoint = new PointF(); private android.graphics.Matrix mImageMatrix = new android.graphics.Matrix(); private android.graphics.Matrix currentMatrix = new android.graphics.Matrix(); private PointF mindPoint; private float overDistance; public NixoImageView2(Context context) { this(context,null); setOnTouchListener(this); } public NixoImageView2(Context context, AttributeSet attrs) { this(context, attrs,0); setOnTouchListener(this); } public NixoImageView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK){ case MotionEvent.ACTION_DOWN: mode = MODE_DRAWE; currentMatrix.set(getImageMatrix()); firstPoint.set(event.getX(),event.getY()); break; case MotionEvent.ACTION_POINTER_DOWN: mode = MODE_ZOOM; overDistance = distance(event); if(overDistance > 10f){ mindPoint = getMidPoint(event); currentMatrix.set(getImageMatrix()); } break; case MotionEvent.ACTION_MOVE: if(mode == MODE_DRAWE){ float dx = event.getX() - firstPoint.x; float dy = event.getY() - firstPoint.y; mImageMatrix.set(currentMatrix); mImageMatrix.postTranslate(dx,dy); }else if(mode == MODE_ZOOM){ float dx = event.getX() - firstPoint.x; float dy = event.getY() - firstPoint.y; float distance1 = distance(event); if(distance1 > 10f){ float scale = distance1 / overDistance; mImageMatrix.set(currentMatrix); mImageMatrix.postScale(scale,scale,dx,dy); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = MODE_LEAVE; break; } setImageMatrix(mImageMatrix); return true; } private float distance(MotionEvent event){ float dx = (event.getX(0) - event.getX(1)); float dy = (event.getY(0) - event.getY(1)); float distance = (float) Math.sqrt(dx*dx + dy*dy); return distance; } private PointF getMidPoint(MotionEvent event){ float dx = (event.getX(0) - event.getX(1)); float dy = (event.getY(0) - event.getY(1)); return new PointF(dx,dy); } }
下面再放一个带注释的版本可能写的有些乱
public class NixoImageView extends android.support.v7.widget.AppCompatImageView implements View.OnTouchListener{ //记录当前的状态 是拖拉图片,还是放大缩小; private int mode = 0; //拖拉模式 private static final int MODE_DRAG = 1; //放大缩小模式 private static final int MODE_ZOOM = 2; //手指离开模式 private static final int MODE_LEAVE = 0; //第一个手指的坐标 private PointF startPoint = new PointF(); //用于记录拖拉图片移动的距离 private Matrix matrix = new Matrix(); //用于记录图片拖拉的前的矩阵 private Matrix currentMatrix = new Matrix(); //两个手指的开始距离 private float startDis; //两个手指的中间点坐标 private PointF midPoint; public NixoImageView(Context context) { this(context,null); setOnTouchListener(this); } public NixoImageView(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); setOnTouchListener(this); } public NixoImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { //event.getAction() & MotionEvent.ACTION_MASK保证了数据的精确 switch (event.getAction() & MotionEvent.ACTION_MASK){ // MotionEvent.ACTION_DOWN :手指压下屏幕的情况 case MotionEvent.ACTION_DOWN: mode = MODE_DRAG; //记录ImageView当前的移动位置 //getImageMatrix()获取图片的矩阵 currentMatrix.set(getImageMatrix()); //设置起点坐标 startPoint.set(event.getX(),event.getY()); break; //手指在屏幕上移动,该事件就会被不断触发 case MotionEvent.ACTION_MOVE: //拖拉图片 if(mode == MODE_DRAG){ //x轴的移动距离 = 触摸点跟控件的距离 - 开始拖拉的横坐标; float dx = event.getX() - startPoint.x; //y轴的移动距离 = 触摸点跟控件的距离 - 开始拖拉的纵坐标; float dy = event.getY() - startPoint.y; matrix.set(currentMatrix); matrix.postTranslate(dx,dy);//转变(拖拉)后的位置 } //放大缩小图片 else if(mode == MODE_ZOOM){ //两个触点之间的距离 float distance = distance(event); if(distance > 10f){ //如果两个手指距离大于10px float scale = distance / startDis;//得到缩放倍数 matrix.set(currentMatrix); matrix.postScale(scale,scale,midPoint.x,midPoint.y);//参数为X轴和Y轴缩放大小,其次是缩放的中心 } } break; //第一个手指离开屏幕 case MotionEvent.ACTION_UP: //其他触点也离开屏幕 case MotionEvent.ACTION_POINTER_UP: mode = MODE_LEAVE; break; //当屏幕上已经有触点(手指) ,再有一个触点压下屏幕(也就是第二个手指压下屏幕) case MotionEvent.ACTION_POINTER_DOWN: mode = MODE_ZOOM; //将两个触点之间的距离传递给startDis startDis = distance(event); if(startDis > 10f){ midPoint = mid(event); currentMatrix.set(getImageMatrix()); } break; } setImageMatrix(matrix); return true; } //计算连个手指间的距离 private float distance(MotionEvent event){ float dx = (event.getX(1) - event.getX(0))/2; float dy = (event.getY(1) - event.getY(0))/2; return (float) Math.sqrt(dx*dx + dy*dy);//勾股定理求出两个触点之间的距离 } private PointF mid(MotionEvent event){ float midX = (event.getX(1) + event.getX(0)) / 2; float midY = (event.getY(1) + event.getY(0)) / 2; return new PointF(midX,midY); } }