本文讲述的手势缩放ImageView,主要使用的是Matrix矩阵,具体matrix的详解大家可以百度,我这里只是简单的介绍几个。
mscale_x和mscale_y分别为x和y的缩放倍数,mtrans_x和mtrans_y分别为x和y的平移量。手势缩放用到的数组中的几个,我这里实现的缩放除了基本的缩放外还做了边界不出界处理和缩放倍数的控制,缩放的倍数不能大于3倍和倍数不能小于原本的大小若小于原本的大小那么恢复原状。(这里大家可以根据需求自己修改是否加上限制)
我先说一下主要的步骤顺序,然后在最后贴上代码:
一、初始化的时候获取原本的图片的矩阵,便于后来的恢复原状
二、复写onTouchEvent()
(1) action_down :这里获取一个当前按下手指时的图片Matrix,然后记录按下的手指的横纵坐标
(2)action_pointer_down:两个手指按下,那么更改当前的模式为缩放。计算两个手指的距离并记录中间点坐标,设置当前的matrix ,由于在action_down 中记录了一个matrix 但是由于第二个手指按下之前可能做了平移等操作所以当前的matrix 必须重新赋值
(2)action_move : 如果是模式拖动图片那么 得到xy的移动距离,根据xy的移动距离和当前矩阵计算,移动后x和y是否超出图片的边界。若超出那么给一个可以正好移动到边界的变化量,重新赋值给 dx和dy,然后做平移操作。
如果是缩放模式 那么用matrix 进行图片的缩放
(3)action_up: 判断当前的图片是否缩小到小于原图,若小于原图那么设置matrix 为原始matrix 恢复原状
由于能里有限可能叙述的不是很明确,请大家原谅下面贴上详细注释的 代码 ,详细注释的 代码 ,详细注释的 代码 :
<span> </span>public class ScaleImageview extends ImageView {
/** 记录是拖拉模式还是放大缩小*/
private int mode = 0;// 初始状态
/** 拖拉模式 */
private static final int MODE_DRAG = 1;
/** 放大缩小模式 */
private static final int MODE_ZOOM = 2;
/** 用于记录开始时候的坐标位置 */
private PointF startPoint = new PointF();
/** 用于记录拖拉图片移动的坐标位置 */
private Matrix matrix = new Matrix();
/** 用于记录图片要进行拖拉时候的坐标位置 */
private Matrix currentMatrix = new Matrix();
/** 记录最开始的图片坐标位置 */
private Matrix originalMatrix=new Matrix();
/** 两个手指的开始距离 */
private float startDis;
/** 两个手指的中间点 */
private PointF midPoint;
//缩放 倍数过小那么不能缩放
private boolean isScale=true;
public ScaleImageview(Context context){
super(context);
init();
}
/**
* 该构造方法在静态引入XML文件中是必须的
* @param context
* @param paramAttributeSet
*/
public ScaleImageview(Context context,AttributeSet paramAttributeSet){
super(context,paramAttributeSet);
init();
}
private void init() {
//记录最开始的图片位置 当缩小倍数
//大约为1的时候 位置边界 出界后 还原为最开始位置
originalMatrix=getImageMatrix();
setScaleType(ScaleType.MATRIX);
}
public boolean onTouchEvent(MotionEvent event) {
float[] values=new float[9];
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 手指压下屏幕
case MotionEvent.ACTION_DOWN:
mode = MODE_DRAG;
// 记录ImageView当前的移动位置
currentMatrix.set(getImageMatrix());
startPoint.set(event.getX(), event.getY());
break;
// 手指在屏幕上移动,改事件会被不断触发
case MotionEvent.ACTION_MOVE:
// 拖拉图片
if (mode == MODE_DRAG) {
float dx = event.getX() - startPoint.x; // 得到x轴的移动距离
float dy = event.getY() - startPoint.y; // 得到x轴的移动距离
// 在没有移动之前的位置上进行移动
matrix.set(currentMatrix);
//检查当前的位置移动偏移量后是否超出image的边界
float[] position=new float[9];
matrix.getValues(position);
dx=checkXBounds(dx,position);
dy=checkYBounds(dy,position);
matrix.postTranslate(dx, dy);
scale(values);
}
// 放大缩小图片
else if (mode == MODE_ZOOM) {
matrix.getValues(values);
float endDis = distance(event);// 结束距离
if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10
float scale = endDis / startDis;// 得到缩放倍数
matrix.set(currentMatrix);
matrix.postScale(scale, scale,midPoint.x,midPoint.y);
matrix.getValues(values);
scale(values);
}
}
break;
// 手指离开屏幕
case MotionEvent.ACTION_UP:
float[] up_position=new float[9];
getImageMatrix().getValues(up_position);
//抬起手指判断当前的图片缩放倍数小于1由于缩放期间可能倍数为1.1-1.0 那么为了截图计算准确 ,这些情况都还原图片为原图大小和位置
if(up_position[0]<1.0f){
Log.i("tran", "复原");
setImageMatrix(originalMatrix);
}
break;
// 当触点离开屏幕,但是屏幕上还有触点(手指)
case MotionEvent.ACTION_POINTER_UP:
mode = 0;
break;
// 当屏幕上已经有触点(手指),再有一个触点压下屏幕
case MotionEvent.ACTION_POINTER_DOWN:
mode = MODE_ZOOM;
/** 计算两个手指间的距离 */
startDis = distance(event);
/** 计算两个手指间的中间点 */
if (startDis > 10f) { // 两个手指并拢在一起的时候像素大于10
midPoint = mid(event);
//记录当前ImageView的缩放倍数
currentMatrix.set(getImageMatrix());
}
break;
}
return true;
}
//缩放或者移动
private void scale(float[] values) {
//matrix 矩阵一个length为9的数组 , 在数组内添加位置坐标 0 代表x缩放的倍数 4 代表y缩放倍数
//由于 起始的2 和5 数值都为0,虽然2 代表x轴方向移动的距离,5 代表y轴方向移动的距离 但是可以理解为平移后的x,y坐标。
//获取缩放后的bitmap 位图的宽高 可以用 原图的宽高 * [数组]0表示x缩放倍数。4代表y缩放倍数 ,但是由于当前xy缩放倍数相同所以一样。
matrix.getValues(values);
//根据需求 为了减少在用该自定义控件 截取 图片的时候 的取坐标难度 放大倍数小于3 。
if(values[0]<=3.00f){
setImageMatrix(matrix);
}
}
//检查 y轴 移动后上下 是否超出边界
private float checkYBounds(float dy, float[] position) {
float tranTop=position[5]+dy;
float tranBottom=position[5]+position[0]*getHeight()+dy;
Log.i("tran", position[2]+"&&"+position[5]);
if(tranTop<=0){
if(tranBottom>=getHeight()){
return dy;
}else {
return -(position[5]+position[4]*getHeight()-getHeight());
}
}else{
return -position[5];
}
}
//检查x轴移动后左右是否超出边界
private float checkXBounds(float dx, float[] position) {
float tranLeft=position[2]+dx;
float tranRight=position[2]+position[0]*getWidth()+dx;
if(tranLeft<=0){
if(tranRight>=getWidth()){
return dx;
}else {
//计算当前right 与宽度的差值
//加个负号 表示左移这些单位 正好到达右侧边界
return -(position[2]+position[0]*getWidth()-getWidth());
}
}else{
return -position[2];
}
}
/** 计算两个手指间的距离 */
private float distance(MotionEvent event) {
float dx = event.getX(1) - event.getX(0);
float dy = event.getY(1) - event.getY(0);
/** 使用勾股定理返回两点之间的距离 */
return FloatMath.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);
}
}
ok 第一篇博客。请多多包涵