作为一个菜鸟,我之前研究自定义ImageView的时候也参考了不少资料,从刚开始的不懂到懵懵懂懂到理解到实现自定义ImageView单点缩放回弹、拖拽、多点缩放功能,在这里我把我研究过程中的一些经验总结下,希望能够使后面的人少走一些弯路。
其实想要实现这些功能,关键就在于onTouchEvent的重写。下面我们就一个一个功能的来进行解析:
一、单点缩放回弹
先来看下效果图:
这个功能实现起来非常简单,无非就是按下的时候将图片缩小,松开的时候将图片还原到原来的大小就ok了。下面来看具体的实现步骤:
1、获取图片的宽和高;
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mWidth = getWidth() - getPaddingLeft() - getPaddingRight();
mHeight = getHeight() - getPaddingTop() - getPaddingBottom();
mCenterWidth = mWidth / 2;
mCenterHeight = mHeight / 2;
}
}
首先重写onLayout方法,在里面获取图片的宽(mWidth)和高(mHeight),至于mCenterWidth和mCenterHeight是后面设置缩放中心点用的。
2、重写onTouchMove方法;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
/** 单点缩放 */
matrix.postScale(mMinScale, mMinScale, mCenterWidth, mCenterHeight);
setImageMatrix(matrix);
break;
case MotionEvent.ACTION_UP:
/** 单点缩放回弹 */
matrix.postScale(1 / mMinScale, 1 / mMinScale, mCenterWidth,
mCenterHeight);
setImageMatrix(matrix);
break;
}
return true;
}
代码中的mCenterWidth和mCenterHeight是指图片缩放时的中心轴点,我这里是以图片中心进行缩放的,根据需求你可以自己进行调整。到这里单点缩放回弹就已经实现了,逻辑非常简单明了。
二、拖拽
在实现这个功能之前,我们必须要理解event.getX()、event.getY()、event.getRawX()、event.getRawY()、getLeft()、getTop()、getRight()、getBottom()、layout(l,t,r,t)这几个方法。
首先我们通过一个图来理解一下这些方法:
假设图中偏左上的图1为原图,右下的图2为移动后的图。
event.getX():获取的是手指触碰的点相对于图片本身的x轴方向的距离,即图中的x。
event.getY():获取的是手指触碰的点相对于图片本身的y轴方向的距离,即图中的y。
event.getRawX():获取的是手指触碰的点相对于屏幕左边的距离,即图中的RawX。
event.getRawY():获取的是手指触碰的点相对于屏幕上边的距离,即图中的RawY。
getLeft():获取的是图1左边缘到屏幕左边的距离。
getTop():获取的是图1上边缘到屏幕上边的距离。
getRight():获取的是图1右边缘到屏幕左边的距离。如果你以为是右边缘到屏幕右边的距离,那你就掉坑了
getBottom():获取的是图1下边缘到屏幕上边的距离。同上
layout(l,t,r,b):这个方法是根据l,t,r,b四个参数来确定图片的位置并进行绘制。l:图片左边缘到屏幕左边缘的距离;t:图片上边缘到屏幕上边缘的距离;r:图片右边缘到屏幕左边缘的距离;b:图片下边缘到屏幕上边缘的距离。
好了,如果你将这些方法理解了,那么下面就可以开始实现拖拽的功能了,先上代码:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
/** 拖拽移动 */
mode = MODE.DRAG;
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
onTouchMove(event);
break;
}
return true;
}
/**
* 移动时的处理方法
*/
private void onTouchMove(MotionEvent event) {
/** 拖拽移动 */
if (mode == MODE.DRAG) {
int dx = (int) event.getRawX() - lastX; // 移动的距离
int dy = (int) event.getRawY() - lastY; // 移动的距离
int left = this.getLeft() + dx;
int top = this.getTop() + dy;
int right = this.getRight() + dx;
int bottom = this.getBottom() + dy;
if (left < 0) {
left = 0;
right = left + this.getWidth();
}
if (right > mScreenWidth) {
right = mScreenWidth;
left = right - this.getWidth();
}
if (top < 0) {
top = 0;
bottom = top + this.getHeight();
}
if (bottom > mScreenHeight) {
bottom = mScreenHeight;
top = bottom - this.getHeight();
}
this.layout(left, top, right, bottom);
<span style="color:#FF0000;">lastX = (int) event.getRawX();
lastY = (int) event.getRawY();</span>
}
}
如果你理解了上面我说的那些方法,那么拖拽的代码逻辑就很好理解了。
1、响应MotionEvent.ACTION_DOWN事件,通过getRawX()、getRawY()方法获取该点距离屏幕左边缘和上边缘的距离
2、响应MotionEvent.ACTION_MOVE事件,再通过getRawX()、getRawY()方法获取此时该点距离屏幕左边缘和上边缘的距离,分别用这两个距离减去按下事件时获取的距离,得到的就是移动x、y轴方向的距离
3、通过移动的距离就可以计算出现在图片的位置了
int left = this.getLeft() + dx;
int top = this.getTop() + dy;
int right = this.getRight() + dx;
int bottom = this.getBottom() + dy;
当然,我们不要忘了对图片超出屏幕的情况进行处理,代码很清楚了,应该不需要解释了。
需要注意的是,在使用load(l,t,r,b)方法重新绘制了图片之后,一定要将移动后的距离屏幕左边缘、上边缘的距离赋值给移动前的变量。
三、多点缩放
这个先直接上代码吧
@Override
public boolean onTouchEvent(MotionEvent event) {
/** 处理单点、多点触摸 **/
switch (event.getAction() <span style="color:#FF0000;">&</span> <span style="color:#FF0000;">MotionEvent.ACTION_MASK</span>) {
case MotionEvent.ACTION_DOWN:
break;
case <span style="color:#FF0000;">MotionEvent.ACTION_POINTER_DOWN</span>:
/** 多点触摸缩放 */
onPointerDown(event);
break;
case MotionEvent.ACTION_MOVE:
onTouchMove(event);
break;
}
return true;
}
/**
* 两个手指响应 MotionEvent.ACTION_POINTER_DOWN时的处理方法
*/
private void onPointerDown(MotionEvent event) {
if (event.getPointerCount() == 2) {
mode = MODE.ZOOM;
beforeLenght = getDistance(event);
}
}
/**
* 移动时的处理方法
*/
private void onTouchMove(MotionEvent event) {
if (mode == MODE.ZOOM) {
/** 多点触摸缩放 */
if (event.getPointerCount() == 2) {
afterLenght = getDistance(event);
matrix.postScale(afterLenght / beforeLenght, afterLenght
/ beforeLenght, mCenterWidth, mCenterHeight);
setImageMatrix(matrix);
<span style="color:#FF0000;">beforeLenght = afterLenght;</span>
}
}
}
首先,要想响应多点触屏事件,上面红色字体是必不可少的。
由上面的代码我们可以看到MotionEvent.ACTION_DOWN事件中是没有进行任何处理的,在MotionEvent.ACTION_POINTER_DOWN事件响应中,首先判断了图片触碰的点的个数,如果为2,则表示是多点触屏事件。这个判断是必不可少的,因为如果没有这个判断,下面的计算两点之间距离就会报错。在MotionEvent.ACTION_MOVE事件响应中,获取移动后两点之间的距离,然后根据移动前后计算出缩放比,在对图片进行缩放处理。当然,缩放处理后一定要主要将移动后的两点间距离赋值给移动前两点间距离。
好吧,终于写完了,因为这是我的第一篇博文,所以感觉写的很乱,还请见谅!