Android Matrix的理解

news2024/9/19 22:10:27

文章目录

  • 前言
  • 一.基础
    • 1.1 Matrix
    • 1.2 使用Matrix的准备知识
  • 二.preXXX和postXXX
    • 2.1 右乘和左乘
    • 2.2 验证规律
  • 三.坐标原点
  • 结束

前言

Android绘制中最重要的要算Matrix类了,同时也是不太好理解的。以前也用过,但是掌握的也不是太好,刚好有时间好好的总结下。

一.基础

1.1 Matrix

Matrix既矩阵,数学里面很重要的工具,在安卓中用于做坐标转换,本篇博文主要记录一下Android中Matrix对于图片的preXXX,postXXX相关API的使用,对于矩阵的数学原理不做过多的深究。

Android关于Matrix最经典的一张图片:
Matrix
那么对于上图的总结:

  • 缩放(Scale)
    对应 MSCALE_XMSCALE_Y
  • 位移(Translate)
    对应 MTRANS_XMTRANS_Y
  • 错切(Skew)
    对应 MSKEW_XMSKEW_Y
  • 旋转(Rotate)
    旋转没有专门的数值来计算,Matrix 会通过计算缩放与错切来处理旋转。

1.2 使用Matrix的准备知识

完全了解图片的坐标原点概念,对于使用Matrix非常重要,对这个概念,有以下两点总结:

  • 所有的操作(旋转、平移、缩放、错切)默认都是以坐标原点为基准点的。
  • 之前操作的坐标系状态会保留,并且影响到后续状态。

举个栗子:

canvas.drawBitmap(mBitmap, 0, 0 , null);

图片会在画布的左上角绘制,此时原点即为画布的左上角,也是图片自身的左上角。
演示结果
红色箭头所指之处便是原点。
实际上坐标原点就是图片自身的左上角,对于这一点可以做个试验看一下:
试验代码:

private void drawMatrix(Canvas canvas){
		// 原图
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        // 从mBitmap中copy出来一张新的bitmap
        Bitmap newBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, true);
        // 居中位置
        float px = (getWidth() - mBitmap.getWidth()) / 2.0f;
        float py = (getHeight() - mBitmap.getHeight()) / 2.0f;
        // 原图移动位置后再顺时针旋转90度
        mLocationMatrix.preTranslate(px, py);
        mLocationMatrix.preRotate(90);
        // draw原图和新图
        canvas.drawBitmap(mBitmap, mLocationMatrix, null);
        // 新图居中显示
        canvas.drawBitmap(newBitmap, px, py, null);
}

运行结果:
演示结果
通过运行结果,可以看出中间的图片确实是绕着自身的左上角顺时针旋转了90度。
有了上面的概念,就可以继续学习Matrix的preXXXpostXXX相关API了。

二.preXXX和postXXX

2.1 右乘和左乘

对于我而言,学习Matrix最为头疼的是搞清楚诸如preTranslate和postTranslate,preRotate和postRotate等相关API的前后执行顺序关系。

想要知道这个顺序关系,需要知道涉及矩阵的数学运算,矩阵的右乘和左乘。
直接给出这个运算规律,你需要记住这个规则
(重要)

// tips: 这里的M是原始矩阵,A是诸如平移,缩放等矩阵,M'是结果矩阵
preXXX  : 右乘, M' = M*A (右乘是因为A在右边)
postXXX : 左乘, M' = A*M (左乘是因为A在左边)

令:T=平移,R=旋转,S=缩放

  1. 全是preXXX相关API:
matrix.preTranslate(px, py);
matrix.preRotate(90);
matrix.preScale(0.5f, 0.5f);

根据上面的规则,则可以写成:
M’ = M * T * R * S (右乘)

  1. 全是postXXX相关API:
matrix.postTranslate(px, py);
matrix.postRotate(90);
matrix.postScale(0.5f, 0.5f);

M’ = S * R * T * M (左乘)

  1. 混合操作:
matrix.postTranslate(px, py);
matrix.preRotate(90);
matrix.preScale(0.5f, 0.5f);

M’ = T * M * R * S
不过对于写代码,不建议混合使用,应该要么用前乘,要么用后乘。

那么知道这个有啥用呢?
当我们通过以上规则得到诸如M’ = M * T * R * S结果后,只要去掉M不看,再从左到右的顺序执行,这样的执行顺序就是使用postXXX和preXXX相关API的前后执行顺序。

所以执行的顺序:

  • M’ = M * T * R * S的顺序就是先平移,再旋转,最后缩放。
  • M’ = S * R * T * M的顺序就是先缩放,再旋转,最后平移。

2.2 验证规律

最后验证一下这个规律:
下面的这段代码是想将图片移动到canvas中间,并且还需要图片倒立展示。

		mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
       	// 居中显示
        float px = (getWidth() - mBitmap.getWidth()) / 2.0f;
        float py = (getHeight() - mBitmap.getHeight()) / 2.0f;
        // 先preRote
        matrix.preRotate(180,
                mBitmap.getWidth() / 2.0f,
                mBitmap.getHeight() / 2.0f
        );
        // 后postTranslate
        matrix.postTranslate(px, py);
        canvas.drawBitmap(mBitmap, matrix, null);

上面的代码直观上给人的感觉是先旋转,再平移。实际上,根据上面的左乘和右乘的规律应该是:
M’ = T * M * R先平移到(px, py)的位置,最后按照图片中心点旋转180度。

运行结果:
在这里插入图片描述
成功的将图片居中并且倒立展示。

三.坐标原点

前面说了所有的操作(旋转、平移、缩放、错切)默认都是以坐标原点为基准点的
下面来验证下:

// 相对图片中心点旋转180度
matrix.preRotate(180, mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f);

运行结果:
在这里插入图片描述
matrix在被初始化的时候实际上是一个单位矩阵
在这里插入图片描述
可以看到2和5位置上的值是0,这就意味着没有移动,那么图片的出生地就是canvas的左上角,也就是图片的左上角。旋转的时候是按照图片的中点去旋转角度的。
所以在写代码的时候,如果图片已经平移到canvas的其他位置,同时还要图片旋转一定角度,那么直接对于图片的宽高各自一半进行旋转即可。
类似以下代码:

matrix.preTranslate(px, py);
matrix.preRotate(180, mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f
);

结束

掌握了矩阵的左乘和右乘和坐标原点之后,写起和matrix相关的代码,将会变得非常轻松。祝大家学习进步,早日上岸!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GO语言包相关总结 -引用(本地和远程),自定义,安装,使用

本篇文章总结以下go语言包相关的知识。 目录 一.导入包 (1)常规导入 (2)别名导入 (3)特殊导入 二.自定义包 三.安装自定义包 四.调用自定义包调用 五.获取远程包 六.go中的保留函数 七.实战 - G…

Python自动化测试实战篇(12),一文学完,Pytest 常用11种第三方插件

这些是之前的文章,里面有一些基础的知识点在前面由于前面已经有写过,所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇(1) Python自动化测试实战篇(2) Python自动化测试实战篇&#xff…

解决python-opencv:(-215:Assertion failed) _img.empty() in function ‘cv::imwrite‘在将视频分成帧图片,写入时出现的问题

最近在搞视频检测问题,在用到将视频分帧保存为图片时,图片可以保存,但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行,在检查代码、检查路径等措施均无果后,了解了视频分帧…

rk3399 调试ap6354

电路如下: wifi&#xff1a; 按照rk3399 sdk默认配置&#xff0c;修改相应的引脚 sdio_pwrseq: sdio-pwrseq { compatible "mmc-pwrseq-simple"; clocks <&rk808 1>; clock-names "ext_clock"; pinctrl-nam…

filter功能演示-鉴权、声明缓存

文章目录 Filter定义工作原理Filter所处环节 Demo示例总结 Filter定义 在Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;中&#xff0c;过滤器&#xff08;Filter&#xff09;是一种强大的组件&#xff0c;用于在Web应用程序中拦截和处理传入的请求和响应…

银河麒麟服务器V10 SP1 .Net6.0 开机自启动

开机自动启动&#xff0c;折腾了一小天&#xff0c;设置/etc/init.d/ 、update-rc.d&#xff0c;可能刚开始用&#xff0c;经验不多吧&#xff0c;尝试多种方式我的服务怎么都启动不起来&#xff0c;根据之前nginx和redis的自动启动经验&#xff0c;使用systemd管理服务&#x…

Unity基础 物理系统 刚体组件下的移动.碰撞.触发检测

当在Unity中创建游戏或应用程序时&#xff0c;重力系统是一个非常重要的组成部分。它可以模拟物体受到地球引力的影响&#xff0c;并产生逼真的物理效果。在Unity中&#xff0c;我们可以使用刚体组件和重力向量来控制重力系统。 首先&#xff0c;在Unity中创建一个物体&#xf…

数据表示与数据编码

数据表示与数据编码 数据表示 bit:二进制位 例如:480Mbps(Mb/s) 小写字母b代表bitbyte:字节 1byte8bit 使用大写字母B表示 byte最初从IBM360中开始表示word:字长 在32bit计算机中一个字长为32位&#xff0c;在64bit计算机中一个字长为64位 最早的微处理器字长为4位 章节学习内…

Windows的基本操作

Windows的基本操作 一、用户管理1.1、用户帐户1.2、用户管理 二、网络配置2.1、配置和查看命令 三、常用命令 一、用户管理 1.1、用户帐户 系统中的一种对象&#xff08;用户、组、计算机&#xff09;包含多种属性&#xff0c;如用户名、密码等不同用户帐户的用户名和密码等一…

插入排序--直接插入排序,折半插入排序,希尔排序

插入排序是一种简单直观的排序方法&#xff0c;其基本思想是每次将一个待排序的记录按其关键词大小插入前面已经排好的子序列&#xff0c;直到全部记录插入完成。 一&#xff0c;直接插入排序&#xff1a;从小到大排序 数组序号01234567待排序列4938659776132749第一轮384965…

【数据算法与结构】用按层次顺序遍历二叉树的方法,统计树中具有度为1的结点数目

题目&#xff1a; Qestion: 用按层次顺序遍历二叉树的方法&#xff0c;统计树中具有度为1的结点数目。 数据结构定义 typedef struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right; } TreeNode;样例二叉树的形状 核心代码 // 统计具有度为1的节点数目的…

解决idea默认配置maven仓库地址的问题

我的maven配置地址 一直没注意 jar包什么的全部都是C盘默认的路径&#xff0c;导入项目更改后&#xff0c;再次导入项目 路径还是会变成C盘的路径&#xff1b; 上网搜索找到了解决的办法&#xff1b; 一、File——>New Projects Setup ———>Settings for New Projects…

React hooks之useCallback的使用与性能分析

使用useCallback优化代码 useCallback是对传过来的回调函数优化&#xff0c;返回的是一个函数&#xff1b;useMemo返回值可以是任何&#xff0c;函数&#xff0c;对象等都可以。 简单来说就是返回一个函数&#xff0c;只有在依赖项发生变化的时候才会更新&#xff08;返回一个…

vue项目打包后如何本都部署访问

npm run build生成dist项目后&#xff0c;在windows部署访问。 方式一&#xff1a; 1、新建一个文件夹 进入目录后打开cmd 输入npm init -y 2、输入 npm i express -s 是用于在 Node.js 项目中安装 Express 框架的命令 3、.将项目打包好的dist文件放入其中以及新建一个app.js文…

C++ 二叉搜索树

1. 内容安排说明 二叉树在前面 C 数据结构阶段已经讲过&#xff0c;本节取名二叉树进阶是因为&#xff1a; 1. map 和 set 特性需要 先铺垫二叉搜索树&#xff0c;而二叉搜索树也是一种树形结构 2. 二叉搜索树的特性了解&#xff0c;有助于更好的理解 map 和 set 的特性 …

在JDK17尝鲜Flink1.17

在JDK17尝鲜Flink1.17 前言 还没玩明白老版本&#xff0c;Flink1.17就来了&#xff01;&#xff01;&#xff01;总还是要向前看的。。。 根据官网文档&#xff1a;https://nightlies.apache.org/flink/flink-docs-release-1.17/docs/try-flink/local_installation/ Flink r…

【Excel技巧】如何将一堆文字快速整理成一列表格数据?

在平时的工作中&#xff0c;我们有时候需要把很多零散的分布的内容&#xff08;比如姓名&#xff09;&#xff0c;复制到Excel工作表的单元格内&#xff0c;变成一列。如果一个个复制粘贴&#xff0c;显然太过繁琐。 如何批量快速的完成这一操作呢&#xff1f;只需要下面简单几…

关于排查springboot启动时页面出现404

今天在进行开发时&#xff0c;Contronller代码没有问题&#xff0c;前端html也没问题&#xff0c;发现当浏览器输入localhost:8080时404&#xff0c;于是进行排查发现&#xff0c;SpringbootWebApplication文件放到了子目录下。 springboot的启动文件必须放在父目录下才可以检测…

【Git原理与使用】-- 远程操作

目录​​​​​​​ 理解分布式版本控制系统 远程仓库 新建远程仓库 lssue 与 Pull Request模板文件 知识铺垫 lssue 模板文件 Pull Request模板文件 克隆远程仓库 使用 HTTPS 方式 使用 SSH 方式 第一步&#xff1a;创建SSH Key 向远程仓库推送 过程梳理 实操 …

Java安全——安全提供者

Java安全 安全提供者 在Java中&#xff0c;安全提供者&#xff08;Security Provider&#xff09;是一种实现了特定安全服务的软件模块。它提供了一系列的加密、解密、签名、验证和随机数生成等安全功能。安全提供者基础设施在Java中的作用是为开发人员提供一种扩展和替换标准…