通过自定义ImageView和实现OnTouchListener 来实现图片的手势滑动和缩放功能
本文会使用到的类 : 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);
}
}
上一篇: HDU 2050折线分割平面(递推)