Matrix介绍 : Android Matrix的用法总结(链接:ttp://blog.csdn.net/jdsjlzx/article/details/52741445)

代码验证

前面讲到的各种图像变换的验证代码如下,一共列出了10种情况。如果要验证其中的某一种情况,只需将相应的代码反注释即可。试验中用到的图片:

尺寸为162 x 251

每种变换的结果,请见代码之后的说明。

package com.pat.testtransformmatrix;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnTouchListener;
import android.widget.ImageView;public class TestTransformMatrixActivity extends Activity implements OnTouchListener
{private TransformMatrixView view;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);view = new TransformMatrixView(this);view.setScaleType(ImageView.ScaleType.MATRIX);view.setOnTouchListener(this);setContentView(view);}class TransformMatrixView extends ImageView{private Bitmap bitmap;private Matrix matrix;public TransformMatrixView(Context context){super(context);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);matrix = new Matrix();}@Overrideprotected void onDraw(Canvas canvas){// 画出原图像canvas.drawBitmap(bitmap, 0, 0, null);// 画出变换后的图像canvas.drawBitmap(bitmap, matrix, null);super.onDraw(canvas);}@Overridepublic void setImageMatrix(Matrix matrix){this.matrix.set(matrix);super.setImageMatrix(matrix);}public Bitmap getImageBitmap(){return bitmap;}}public boolean onTouch(View v, MotionEvent e){if(e.getAction() == MotionEvent.ACTION_UP){Matrix matrix = new Matrix();// 输出图像的宽度和高度(162 x 251)Log.e("TestTransformMatrixActivity", "image size: width x height = " +  view.getImageBitmap().getWidth() + " x " + view.getImageBitmap().getHeight());// 1. 平移matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());// 在x方向平移view.getImageBitmap().getWidth(),在y轴方向view.getImageBitmap().getHeight()view.setImageMatrix(matrix);// 下面的代码是为了查看matrix中的元素float[] matrixValues = new float[9];matrix.getValues(matrixValues);for(int i = 0; i < 3; ++i){String temp = new String();for(int j = 0; j < 3; ++j){temp += matrixValues[3 * i + j ] + "\t";}Log.e("TestTransformMatrixActivity", temp);}//          // 2. 旋转(围绕图像的中心点)
//          matrix.setRotate(45f, view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(view.getImageBitmap().getWidth() * 1.5f, 0f);
//          view.setImageMatrix(matrix);//          // 3. 旋转(围绕坐标原点) + 平移(效果同2)
//          matrix.setRotate(45f);
//          matrix.preTranslate(-1f * view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight() / 2f);
//          matrix.postTranslate((float)view.getImageBitmap().getWidth() / 2f, (float)view.getImageBitmap().getHeight() / 2f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate((float)view.getImageBitmap().getWidth() * 1.5f, 0f);
//          view.setImageMatrix(matrix);    //          // 4. 缩放
//          matrix.setScale(2f, 2f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(view.getImageBitmap().getWidth(), view.getImageBitmap().getHeight());
//          view.setImageMatrix(matrix);//          // 5. 错切 - 水平
//          matrix.setSkew(0.5f, 0f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(view.getImageBitmap().getWidth(), 0f);
//          view.setImageMatrix(matrix);//          // 6. 错切 - 垂直
//          matrix.setSkew(0f, 0.5f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(0f, view.getImageBitmap().getHeight());
//          view.setImageMatrix(matrix);//          7. 错切 - 水平 + 垂直
//          matrix.setSkew(0.5f, 0.5f);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(0f, view.getImageBitmap().getHeight());
//          view.setImageMatrix(matrix);//          // 8. 对称 (水平对称)
//          float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
//          matrix.setValues(matrix_values);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(0f, view.getImageBitmap().getHeight() * 2f);
//          view.setImageMatrix(matrix);//          // 9. 对称 - 垂直
//          float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
//          matrix.setValues(matrix_values);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(view.getImageBitmap().getWidth() * 2f, 0f);
//          view.setImageMatrix(matrix);//          // 10. 对称(对称轴为直线y = x)
//          float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
//          matrix.setValues(matrix_values);
//          // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
//          matrix.postTranslate(view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth(),
//                  view.getImageBitmap().getHeight() + view.getImageBitmap().getWidth());
//          view.setImageMatrix(matrix);view.invalidate();}return true;}
}

下面给出上述代码中,各种变换的具体结果及其对应的相关变换矩阵

  • 平移 
     
    输出的结果: 
     
    请对照第一部分中的“一、平移变换”所讲的情形,考察上述矩阵的正确性。

  • 旋转(围绕图像的中心点) 
     
    输出的结果: 
     
    它实际上是 
    matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f); 
    matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f); 
    这两条语句综合作用的结果。根据第一部分中“二、旋转变换”里面关于围绕某点旋转的公式, 
    matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f); 
    所产生的转换矩阵就是: 
     
    而matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);的意思就是在上述矩阵的左边再乘以下面的矩阵: 
     
    关于post是左乘这一点,我们在前面的理论部分曾经提及过,后面我们还会专门讨论这个问题。

所以它实际上就是: 
 
出去计算上的精度误差,我们可以看到我们计算出来的结果,和程序直接输出的结果是一致的。


  • 旋转(围绕坐标原点旋转,在加上两次平移,效果同2) 
     
    根据第一部分中“二、旋转变换”里面关于围绕某点旋转的解释,不难知道: 
    matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f); 
    等价于 
    matrix.setRotate(45f); 
    matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f); 
    matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);

其中matrix.setRotate(45f)对应的矩阵是: 
 
matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight()/ 2f)对应的矩阵是: 
 
由于是preTranslate,是先乘,也就是右乘,即它应该出现在matrix.setRotate(45f)所对应矩阵的右侧。

matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f)对应的矩阵是: 
 
这次由于是postTranslate,是后乘,也就是左乘,即它应该出现在matrix.setRotate(45f)所对应矩阵的左侧。

所以综合起来, 
matrix.setRotate(45f); 
matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f); 
matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f); 
对应的矩阵就是: 
 
这和下面这个矩阵(围绕图像中心顺时针旋转45度)其实是一样的: 
 
因此,此处变换后的图像和2中变换后的图像时一样的。


  • 缩放变换 
     
    程序所输出的两个矩阵分别是: 
     
    其中第二个矩阵,其实是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“三、缩放变换”和“一、平移变换”说法,自行验证结果。

  • 错切变换(水平错切) 
     
    代码所输出的两个矩阵分别是: 
     
    其中,第二个矩阵其实是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

  • 错切变换(垂直错切) 
     
    代码所输出的两个矩阵分别是: 
     
    其中,第二个矩阵其实是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

  • 错切变换(水平+垂直错切) 
     
    代码所输出的两个矩阵分别是: 
     
    其中,后者是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“四、错切变换”和“一、平移变换”的相关说法,自行验证结果。

  • 对称变换(水平对称) 
     
    代码所输出的两个各矩阵分别是: 
     
    其中,后者是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

  • 对称变换(垂直对称) 
     
    代码所输出的两个矩阵分别是: 
     
    其中,后者是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

  • 对称变换(对称轴为直线) 
     
    代码所输出的两个矩阵分别是: 
     
    其中,后者是下面两个矩阵相乘的结果: 
     
    大家可以对照第一部分中的“五、对称变换”和“一、平移变换”的相关说法,自行验证结果。

  • 关于先乘和后乘的问题 
    由于矩阵的乘法运算不满足交换律,我们在前面曾经多次提及先乘、后乘的问题,即先乘就是矩阵运算中右乘,后乘就是矩阵运算中的左乘。其实先乘、后乘的概念是针对变换操作的时间先后而言的,左乘、右乘是针对矩阵运算的左右位置而言的。以第一部分“二、旋转变换”中围绕某点旋转的情况为例: 
     
    越靠近原图像中像素的矩阵,越先乘,越远离原图像中像素的矩阵,越后乘。事实上,图像处理时,矩阵的运算是从右边往左边方向进行运算的。这就形成了越在右边的矩阵(右乘),越先运算(先乘),反之亦然。

当然,在实际中,如果首先指定了一个matrix,比如我们先setRotate(),即指定了上面变换矩阵中,中间的那个矩阵,那么后续的矩阵到底是pre还是post运算,都是相对这个中间矩阵而言的。

所有这些,其实都是很自然的事情。

实际应用

下面我们结合之前的介绍和 Android 手势结合起来,利用各种不同的手势对图像进行平移、缩放和旋转。
1. 在AndroidManifest.xml中增加权限: <uses-permissionandroid:name= "android.permission.VIBRATE" />
2.自定义PatImageView
package com.pat.imageview;import android.app.Service;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Vibrator;
import android.util.FloatMath;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;public class PatImageView extends ImageView
{private Matrix matrix;private Matrix savedMatrix;private boolean long_touch = false;private static int NONE = 0;private static int DRAG = 1;    // 拖动private static int ZOOM = 2;  // 缩放private static int ROTA = 3;  // 旋转private int mode = NONE;private PointF startPoint;private PointF middlePoint;private float oldDistance;private float oldAngle;private Vibrator vibrator;private GestureDetector gdetector;public PatImageView(final Context context){super(context);matrix = new Matrix();savedMatrix = new Matrix();matrix.setTranslate(0f, 0f);setScaleType(ScaleType.MATRIX);setImageMatrix(matrix);startPoint = new PointF();middlePoint = new PointF();oldDistance = 1f;gdetector = new GestureDetector(context, new GestureDetector.OnGestureListener(){@Overridepublic boolean onSingleTapUp(MotionEvent e){return true;}@Overridepublic void onShowPress(MotionEvent e){}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){return true;}@Overridepublic void onLongPress(MotionEvent e){long_touch = true;vibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);// 振动50ms,提示后续的操作将是旋转图片,而非缩放图片vibrator.vibrate(50);}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY){return true;}@Overridepublic boolean onDown(MotionEvent e){return true;}});setOnTouchListener(new OnTouchListener(){public boolean onTouch(View view, MotionEvent event){switch(event.getAction() & MotionEvent.ACTION_MASK){case MotionEvent.ACTION_DOWN:          // 第一个手指touchsavedMatrix.set(matrix);startPoint.set(event.getX(), event.getY());mode = DRAG;long_touch = false;break;case MotionEvent.ACTION_POINTER_DOWN:    // 第二个手指toucholdDistance = getDistance(event); // 计算第二个手指touch时,两指之间的距离oldAngle = getDegree(event);        // 计算第二个手指touch时,两指所形成的直线和x轴的角度if(oldDistance > 10f){savedMatrix.set(matrix);middlePoint = midPoint(event);if(!long_touch){mode = ZOOM;}else{mode = ROTA;}}break;case MotionEvent.ACTION_UP:mode = NONE;break;case MotionEvent.ACTION_POINTER_UP:mode = NONE;break;case MotionEvent.ACTION_MOVE:if(vibrator != null)    vibrator.cancel();if(mode == DRAG){matrix.set(savedMatrix);matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);}if(mode == ZOOM){float newDistance = getDistance(event);if(newDistance > 10f){matrix.set(savedMatrix);float scale = newDistance / oldDistance;matrix.postScale(scale, scale, middlePoint.x, middlePoint.y);}}if(mode == ROTA){float newAngle = getDegree(event);matrix.set(savedMatrix);float degrees = newAngle - oldAngle;matrix.postRotate(degrees, middlePoint.x, middlePoint.y);}break;}setImageMatrix(matrix);invalidate();gdetector.onTouchEvent(event);return true;}});}// 计算两个手指之间的距离private float getDistance(MotionEvent event){float x = event.getX(0) - event.getX(1);float y = event.getY(0) - event.getY(1);return FloatMath.sqrt(x * x + y * y);}// 计算两个手指所形成的直线和x轴的角度private float getDegree(MotionEvent event){return (float)(Math.atan((event.getY(1) - event.getY(0)) / (event.getX(1) - event.getX(0))) * 180f);}// 计算两个手指之间,中间点的坐标private PointF midPoint( 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;}
}

PatImageViewActivity.java代码如下:

package com.pat.imageview;import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;public class PatImageViewActivity extends Activity
{@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);PatImageView piv = new PatImageView(this);Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);piv.setImageBitmap(bmp);setContentView(piv);}
}

由于有些手势在模拟器上无法模拟,所以就不上运行结果的图片了。本人在真机上运行后(照片就不拍了,有点累啦),可以轻松做到:

1.     很方便地拖动图片(比如,单指按住屏幕进行拖动)

2.     很方便地缩放图片(比如,双指按住屏幕进行分开或者并拢操作,可分别实现放大或者缩小图片的功能)

3.     长按出现振动后,可以很方便地旋转图片(一个手指固定,另外一个手指围绕那个固定的手指运动)。

转自:http://blog.csdn.net/pathuang68/article/details/6991988

Android Matrix的代码验证和应用相关推荐

  1. Android Matrix

    http://www.cnblogs.com/qiengo/archive/2012/06/30/2570874.html Matrix的数学原理 平移变换 旋转变换 缩放变换 错切变换 对称变换 代 ...

  2. 了解Android Matrix转换

    原文 了解Android Matrix转换 很多年前,在学校我学习了矩阵. 我记不太清楚了,但我记得的是在想,"但是......你对这些知识做了什么呢?" 快进几年,我开始作为An ...

  3. android Matrix图片变换处理

    今天,讲讲android  Matrix图片变换处理的内容. Matrix 对于一个图片变换的处理,需要Matrix类的支持,它位于"android.graphics.Matrix&qu ...

  4. android计算dpi代码_android计算pad或手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法...

    手机分辨率基础知识(DPI,DIP计算) 1.术语和概念 术语 说明 备注 Screen size(屏幕尺寸) 指的是手机实际的物理尺寸,比如常用的2.8英寸,3.2英寸,3.5英寸,3.7英寸 摩托 ...

  5. android加载efi分区,高通Android UEFI XBL 代码流程分析

    高通Android UEFI XBL 代码流程分析 背景 之前学习的lk阶段点亮LCD的流程算是比较经典,但是高通已经推出了很多种基于UEFI方案的启动架构. 所以需要对这块比较新的技术进行学习.在学 ...

  6. android 自由缩放图片大小,android Matrix实现图片随意放大缩小或拖动

    本文实例为大家分享了android Matrix图片随意放大缩小和拖动的具体代码,供大家参考,具体内容如下 step1:新建一个项目DragAndZoom,并准备一张照片放在res/drawable- ...

  7. android 常用混淆,Android常用的代码混淆整理【原创】

    android里的代码混淆是比不可少的东西,下面就贴一些常用的代码混淆. #指定代码的压缩级别 -optimizationpasses 5 #包明不混合大小写 -dontusemixedcasecla ...

  8. 【Android 逆向】代码调试器开发 ( 使用 NDK 中的 ndk-build + Android.mk 编译 Android 平台的代码调试器可执行应用 )

    文章目录 一.Android 平台代码调试器代码 二.Android.mk 构建脚本内容 三.Application.mk 构建脚本内容 四.正式编译 五.博客资源 一.Android 平台代码调试器 ...

  9. 【Android 逆向】代码调试器开发 ( 等待进程状态改变 | detach 脱离进程调试 PTRACE_DETACH | 调试中继续运行程序 PTRACE_CONT )

    文章目录 一.等待进程状态改变 二.detach 脱离进程调试 PTRACE_DETACH 三.调试中继续运行程序 PTRACE_CONT 一.等待进程状态改变 上一篇博客 [Android 逆向]代 ...

最新文章

  1. 宇宙膨胀背后的故事(卅三):宇宙之有生于无
  2. Android Studio 打开后无故爆红后解决办法
  3. C 语言编程 — 变量和常量
  4. Tensorflow-gpu 在Anaconda中使用出现问题的解决方式
  5. 算法学习的链接(持续更新)
  6. java导出类_java导出excel工具类
  7. R:matlab交互,数据调用
  8. Git 之五 通信协议(HTTPS、SSH、Git)、使用远程仓库(GitHub、GitLab、Gitee等)
  9. 7.2-5 usermod
  10. js中闭包的概念和用法
  11. 一步步编写操作系统 32 linux内核获取内存容量的方法
  12. mysql hy093_请问SQLSTATE [HY093]:参数号无效:未定义参数
  13. 测试自己幸运数字的软件,心理测试:选一个你的幸运数字,测一下你最近会有什么好事发生?...
  14. FatFs源码剖析(转)
  15. 线性代数-求解地球法线
  16. 史上最简单的SpringCloud教程 | 第九篇: 服务链路追踪(Spring Cloud Sleuth)(Finchley版本)...
  17. Mysql 如何做双机热备和负载均衡 (方法一)
  18. java gson解析JSON
  19. 高德地图记录跑步轨迹_朋友圈晒跑步 亲测高德地图和百度地图哪个更实用
  20. linux kettle命令,Linux下用命令来执行kettle文件资源库的文件ktr与kjb的方法

热门文章

  1. nacos的配置中心
  2. Qt之图形(QPainter的基本绘图)
  3. 谈基于机器智能(知识)的机器翻译
  4. EEGNet:一个小型的卷积神经网络,用于基于脑电的脑机接口
  5. CocosCreator游戏资源加载assetManager
  6. IT大厂有两个月的实习生吗?IT毕业怎么找实习单位?
  7. 随时随地管理企业电脑安全,腾讯电脑管家「小团队版」重磅来袭
  8. oracle待摊费用改为一次性摊销,长期待摊费用可以一次性转入损益吗
  9. C#上位机,信捷XD系列modbus485通信例子
  10. 判断cpu大小端模式(c++代码实现)