Android实现对图片放大、平移和旋转的功能

先来看看要实现的效果图

在讲解中,需要大家提前了解一些关于图片绘制的原理的相关知识。

关于实现的流程

1、自定义View

2、获得操作图片的Bitmap

3、复写View的onTouchEvent()方法中的ACTION_DOWN,ACTION_POINTER_DOWN,ACTION_MOVE,ACTION_POINTER_UP以及ACTION_UP事件。

4、定义相应图片变化的Matrix矩阵,通过手势操作的变化来设置相应的Matrix。

5、完成最终的Matrix设置时,通过invalidate()方法重新绘制页面。

那么接下来我们根据以上流程一步一步实现代码。

代码演示

/** * 作者:ZhouYou * 日期:2016/8/23. */ public class TouchImageView extends View { // 绘制图片的边框 private Paint paintEdge; // 绘制图片的矩阵 private Matrix matrix = new Matrix(); // 手指按下时图片的矩阵 private Matrix downMatrix = new Matrix(); // 手指移动时图片的矩阵 private Matrix moveMatrix = new Matrix(); // 资源图片的位图 private Bitmap srcImage; // 多点触屏时的中心点 private PointF midPoint = new PointF(); // 触控模式 private int mode; private static final int NONE = 0; // 无模式 private static final int TRANS = 1; // 拖拽模式 private static final int ZOOM = 2; // 缩放模式 // 是否超过边界 private boolean withinBorder; public TouchImageView(Context context) { this(context, null); } public TouchImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TouchImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paintEdge = new Paint(); paintEdge.setColor(Color.BLACK); paintEdge.setAlpha(170); paintEdge.setAntiAlias(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); srcImage = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_avatar_1); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float[] points = getBitmapPoints(srcImage, matrix); float x1 = points[0]; float y1 = points[1]; float x2 = points[2]; float y2 = points[3]; float x3 = points[4]; float y3 = points[5]; float x4 = points[6]; float y4 = points[7]; // 画边框 canvas.drawLine(x1, y1, x2, y2, paintEdge); canvas.drawLine(x2, y2, x4, y4, paintEdge); canvas.drawLine(x4, y4, x3, y3, paintEdge); canvas.drawLine(x3, y3, x1, y1, paintEdge); // 画图片 canvas.drawBitmap(srcImage, matrix, null); } // 手指按下屏幕的X坐标 private float downX; // 手指按下屏幕的Y坐标 private float downY; // 手指之间的初始距离 private float oldDistance; // 手指之间的初始角度 private float oldRotation; @Override public boolean onTouchEvent(MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: mode = TRANS; downX = event.getX(); downY = event.getY(); downMatrix.set(matrix); break; case MotionEvent.ACTION_POINTER_DOWN: // 多点触控 mode = ZOOM; oldDistance = getSpaceDistance(event); oldRotation = getSpaceRotation(event); downMatrix.set(matrix); midPoint = getMidPoint(event); break; case MotionEvent.ACTION_MOVE: // 缩放 if (mode == ZOOM) { moveMatrix.set(downMatrix); float deltaRotation = getSpaceRotation(event) - oldRotation; float scale = getSpaceDistance(event) / oldDistance; moveMatrix.postScale(scale, scale, midPoint.x, midPoint.y); moveMatrix.postRotate(deltaRotation, midPoint.x, midPoint.y); withinBorder = getMatrixBorderCheck(srcImage, event.getX(), event.getY()); if (withinBorder) { matrix.set(moveMatrix); invalidate(); } } // 平移 else if (mode == TRANS) { moveMatrix.set(downMatrix); moveMatrix.postTranslate(event.getX() - downX, event.getY() - downY); withinBorder = getMatrixBorderCheck(srcImage, event.getX(), event.getY()); if (withinBorder) { matrix.set(moveMatrix); invalidate(); } } break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: mode = NONE; break; default: break; } return true; } /** * 获取手指的旋转角度 * * @param event * @return */ private float getSpaceRotation(MotionEvent event) { double deltaX = event.getX(0) - event.getX(1); double deltaY = event.getY(0) - event.getY(1); double radians = Math.atan2(deltaY, deltaX); return (float) Math.toDegrees(radians); } /** * 获取手指间的距离 * * @param event * @return */ private float getSpaceDistance(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } /** * 获取手势中心点 * * @param event */ private PointF getMidPoint(MotionEvent event) { PointF point = new PointF(); float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); return point; } /** * 将matrix的点映射成坐标点 * * @return */ protected float[] getBitmapPoints(Bitmap bitmap, Matrix matrix) { float[] dst = new float[8]; float[] src = new float[]{ 0, 0, bitmap.getWidth(), 0, 0, bitmap.getHeight(), bitmap.getWidth(), bitmap.getHeight() }; matrix.mapPoints(dst, src); return dst; } /** * 检查边界 * * @param x * @param y * @return true - 在边界内 | false - 超出边界 */ private boolean getMatrixBorderCheck(Bitmap bitmap, float x, float y) { if (bitmap == null) return false; float[] points = getBitmapPoints(bitmap, moveMatrix); float x1 = points[0]; float y1 = points[1]; float x2 = points[2]; float y2 = points[3]; float x3 = points[4]; float y3 = points[5]; float x4 = points[6]; float y4 = points[7]; float edge = (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); if ((2 + Math.sqrt(2)) * edge >= Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2)) + Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2)) + Math.sqrt(Math.pow(x - x3, 2) + Math.pow(y - y3, 2)) + Math.sqrt(Math.pow(x - x4, 2) + Math.pow(y - y4, 2))) { return true; } return false; } }

我已经在代码中针对可能遇到的问题做了详细的注释。

1. Matrix

// 绘制图片的矩阵 private Matrix matrix = new Matrix(); // 手指按下时图片的矩阵 private Matrix downMatrix = new Matrix(); // 手指移动时图片的矩阵 private Matrix moveMatrix = new Matrix();

首先我定义了三个Matrix变量,目的在于通过不同手势的操控图片的Matrix最终由绘制图片的Matrix所接收,因此需要在不同的操作中使用不同的Matrix进行图形变换的数据传递,从而在渲染页面的时候将最终的Matrix再传递回绘图的Matrix。

2. PointF

// 多点触屏时的中心点 private PointF midPoint = new PointF();

因为如果是针对图片的旋转和放大操作,需要通过两个手指进行控制,因此我们需要知道在多个手指触摸屏幕时的中心点坐标。

3. 触控模式

// 触控模式 private int mode; private static final int NONE = 0; // 无模式 private static final int TRANS = 1; // 拖拽模式 private static final int ZOOM = 2; // 缩放模式

在onTouchEvent()事件中,会根据不同的事件变换触控的模式,从而进行不同图片变换的操作。

4. onTouchEvent()

首先,我们是自定义的View,因此如果要对该事件进行消费的话,需要将返回值设置为true。

(1)ACTION_DOWN - 该事件是单点触屏的事件,也就是说如果一个手指按下屏幕的时候就会回调这个事件。那么我们在该事件中就将触控模式设置为拖拽模式(TRANS),记录下按下屏幕的xy坐标,并在这个事件中将绘图的Matrix复制给按下屏幕的Matrix。

case MotionEvent.ACTION_DOWN: mode = TRANS; downX = event.getX(); downY = event.getY(); downMatrix.set(matrix); break;

(2)ACTION_POINTER_DOWN - 这个事件发生在超过一个手指触摸屏幕的时候。我们在这个事件中即可针对多点触屏的操作进行初始化设置。在该事件中,我们将触控模式重新设置为(ZOOM),初始化两指之间触摸屏幕的距离以及两指之间的旋转角度,初始化两指之间的中心点坐标。最后把绘图的Matrix复制给按下屏幕的Matrix。

case MotionEvent.ACTION_POINTER_DOWN: // 多点触控 mode = ZOOM; oldDistance = getSpaceDistance(event); oldRotation = getSpaceRotation(event); midPoint = getMidPoint(event); downMatrix.set(matrix); break;

(3)ACTION_MOVE - 到了移动的事件中,根据之前的触控模式进行判断。首先,将按下事件的Matrix复制给移动事件的Matrix。如果是(ZOOM)模式,我们将会根据事件获得手指旋转角度的差值,以及手指之间距离的差值。根据这两个差值,以及在ACTION_DOWN事件中获得的中点坐标,我们即可设置MOVE事件的缩放和旋转。(TRANS)模式也是如此。最后通过获取图片变换的边界值来判断是否进行绘图渲染。

case MotionEvent.ACTION_MOVE: // 缩放 if (mode == ZOOM) { moveMatrix.set(downMatrix); float deltaRotation = getSpaceRotation(event) - oldRotation; float scale = getSpaceDistance(event) / oldDistance; moveMatrix.postScale(scale, scale, midPoint.x, midPoint.y); moveMatrix.postRotate(deltaRotation, midPoint.x, midPoint.y); withinBorder = getMatrixBorderCheck(srcImage, event.getX(), event.getY()); if (withinBorder) { matrix.set(moveMatrix); invalidate(); } } // 平移 else if (mode == TRANS) { moveMatrix.set(downMatrix); moveMatrix.postTranslate(event.getX() - downX, event.getY() - downY); withinBorder = getMatrixBorderCheck(srcImage, event.getX(), event.getY()); if (withinBorder) { matrix.set(moveMatrix); invalidate(); } } break;

(4)ACTION_POINTER_UP和ACTION_UP - 在这两个事件中,重新将触屏的模式设置会NONE。

5. 边界判断

以下即为边界判断的逻辑是针对正方形的图片来说的。首先通过原图片相对自己四个坐标映射成为Matrix对应屏幕的点坐标。通过得到4个点的坐标,我们即可根据手指触摸图片时的坐标与图片的4个点坐标进行关联。

边界判断的逻辑是手指触摸图片的点到4个顶点的距离之和如果小于(2+根号2倍)的斜边长度,即视为不超过边界。

/** * 将matrix的点映射成坐标点 * * @return */ protected float[] getBitmapPoints(Bitmap bitmap, Matrix matrix) { float[] dst = new float[8]; float[] src = new float[]{ 0, 0, bitmap.getWidth(), 0, 0, bitmap.getHeight(), bitmap.getWidth(), bitmap.getHeight() }; matrix.mapPoints(dst, src); return dst; } /** * 检查边界 * * @param x * @param y * @return true - 在边界内 | false - 超出边界 */ private boolean getMatrixBorderCheck(Bitmap bitmap, float x, float y) { if (bitmap == null) return false; float[] points = getBitmapPoints(bitmap, moveMatrix); float x1 = points[0]; float y1 = points[1]; float x2 = points[2]; float y2 = points[3]; float x3 = points[4]; float y3 = points[5]; float x4 = points[6]; float y4 = points[7]; float edge = (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); if ((2 + Math.sqrt(2)) * edge >= Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2)) + Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2)) + Math.sqrt(Math.pow(x - x3, 2) + Math.pow(y - y3, 2)) + Math.sqrt(Math.pow(x - x4, 2) + Math.pow(y - y4, 2))) { return true; } return false; }

总结

好了,本文的内容到这就结束了,完成了以上的步骤,即可完成针对图片在屏幕上的放大、平移和旋转的操作。是不是还是很简单的。有兴趣的可以自己动手操作起来,希望这篇文章对大家的学习和工作能有所帮助,如果有疑问可以留言交流,谢谢大家对脚本之家的支持。

时间: 2024-10-27 19:32:22

Android实现对图片放大、平移和旋转的功能的相关文章

Android中imageView图片放大缩小及旋转功能示例代码

一.简介 二.方法 1)设置图片放大缩小效果 第一步:将<ImageView>标签中的android:scaleType设置为"fitCenter" android:scaleType="fitCenter" 第二步:获取屏幕的宽度 DisplayMetrics dm=new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); dm.widthPixels 第三

Android编程实现图片的上传和下载功能示例_Android

本文实例讲述了Android编程实现图片的上传和下载功能.分享给大家供大家参考,具体如下: 在实现一个Android的WEB服务客户端,比如微博,论坛客户端时,经常会使用到图片的上传和下载.在这里介绍如何利用HttpClient实现图片的上传和下载功能. 1 图片上传:上传图片时,首先获得图片的路径,创建文件,并将图片转化为字节流写入到request,并发送该请求. 客户端代码: File file = new File(imageUrl); String httpUrl = httpDomai

Android编程实现图片的上传和下载功能示例

本文实例讲述了Android编程实现图片的上传和下载功能.分享给大家供大家参考,具体如下: 在实现一个Android的WEB服务客户端,比如微博,论坛客户端时,经常会使用到图片的上传和下载.在这里介绍如何利用HttpClient实现图片的上传和下载功能. 1 图片上传:上传图片时,首先获得图片的路径,创建文件,并将图片转化为字节流写入到request,并发送该请求. 客户端代码: File file = new File(imageUrl); String httpUrl = httpDomai

Android实现对图片放大、平移和旋转的功能_Android

先来看看要实现的效果图 在讲解中,需要大家提前了解一些关于图片绘制的原理的相关知识. 关于实现的流程      1.自定义View      2.获得操作图片的Bitmap      3.复写View的onTouchEvent()方法中的ACTION_DOWN,ACTION_POINTER_DOWN,ACTION_MOVE,ACTION_POINTER_UP以及ACTION_UP事件.      4.定义相应图片变化的Matrix矩阵,通过手势操作的变化来设置相应的Matrix.      5.

Android应用中图片浏览时实现自动切换功能的方法详解_Android

先给最终效果图: 当我们在最下边的gallery中切换图片时,上面的大图片会自动切换,切换时有动画效果哦,很简单的一个程序,有待完善更多的功能! activity代码: package cn.com.chenzheng_java; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.V

Android多点触控实现对图片放大缩小平移,惯性滑动等功能_Android

文章将在原有基础之上做了一些扩展功能: 1.图片的惯性滑动 2.图片缩放小于正常比例时,松手会自动回弹成正常比例 3.图片缩放大于最大比例时,松手会自动回弹成最大比例 实现图片的缩放,平移,双击缩放等基本功能的代码如下,每一行代码我都做了详细的注释 public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener , V

Android开发之图片旋转功能实现方法【基于Matrix】

本文实例讲述了Android开发之图片旋转功能实现方法.分享给大家供大家参考,具体如下: 在Android中进行图像旋转需要使用Matrix,它包含了一个3*3的矩阵,专门用于进行图像变换匹配.Matrix ,中文里叫矩阵,高等数学里有介绍,在图像处理方面,主要是用于平面的缩放.平移.旋转等操作.Matrix没有机构体,它必须初始化,然后通过reset方法和set方法来实现. 首先介绍一下矩阵运算.加法和减法就不用说了,太简单了,对应位相加就好.图像处理,主要用到的是乘法 .下面是一个乘法的公式

缩放-android一个图片放大功能的实现,求思路

问题描述 android一个图片放大功能的实现,求思路 就像手机QQ那样,点一张别人发的图片进入一个Activity,可以随意缩小放大那张图片的功能是怎么实现的??? 解决方案 http://www.cnblogs.com/linjzong/p/4211661.html 解决方案二: http://blog.csdn.net/u011596810/article/details/47173785 里面的代码亲测有用,而且是个独立的封装类,又有蛮多的注释,楼主可以尝试看看 解决方案三: 可以看看我

关于android多点触控放大缩小图片请教个问题

问题描述 关于android多点触控放大缩小图片请教个问题 图片是通过sd卡读取的,怎么能初始化在中心啊,怎样把图片放大缩小设定最大最小值,有相关demo吗.谢谢 解决方案 ImageView有个参数设定,可以居中显示 android:scaleType="center"