解释这些方法的差别
时间: 2025-07-19 07:28:45 浏览: 1
<think>我们正在讨论Android中的Matrix类,特别是setRotate、preRotate和postRotate方法的区别,以及px, py参数的作用。
根据引用[2]和[3],我们可以知道:
- setRotate方法会重置Matrix,然后设置旋转变换。也就是说,调用setRotate后,Matrix之前的所有变换都会被清除,然后设置当前的旋转。
- preRotate和postRotate则是在当前Matrix的基础上叠加旋转变换,不会重置Matrix。其中:
preRotate表示先进行旋转,再进行原有的变换(即旋转左乘原矩阵);
postRotate表示先进行原有的变换,再进行旋转(即旋转右乘原矩阵)。
另外,关于旋转中心:
- 不带旋转中心的方法(如setRotate(float degrees))默认以坐标原点(0,0)为旋转中心。
- 带旋转中心的方法(如setRotate(float degrees, float px, float py))会以(px, py)为旋转中心。根据引用[4],其实现原理是:
1. 将旋转中心平移到原点(即平移(-px, -py))
2. 旋转指定角度
3. 将旋转中心平移回原位置(即平移(px, py))
因此,实际上这个旋转操作是三个变换的复合:T(px, py) * R(degrees) * T(-px, -py)
同样,preRotate和postRotate也有带旋转中心坐标的版本,其原理相同,只是这个旋转变换是左乘(pre)还是右乘(post)当前矩阵。
下面我们用一个例子来说明:
假设我们有一个点P,我们想以点C(px, py)为中心旋转degrees度。
使用setRotate(degrees, px, py):
Matrix m = new Matrix();
m.setRotate(degrees, px, py);
这个矩阵m的效果是:先平移(-px, -py),然后旋转,再平移(px, py)。
使用preRotate:
如果我们已经有一个矩阵m(可能包含一些变换),然后我们想以点C为中心旋转,那么我们可以:
m.preTranslate(px, py); // 步骤3:平移回原位置
m.preRotate(degrees); // 步骤2:旋转
m.preTranslate(-px, -py); // 步骤1:平移至原点
或者更简洁地使用:
m.preRotate(degrees, px, py); // 这一句相当于上面三句
使用postRotate:
同样,如果我们要在已有变换之后追加一个旋转(以点C为中心):
m.postTranslate(-px, -py); // 步骤1:平移至原点
m.postRotate(degrees); // 步骤2:旋转
m.postTranslate(px, py); // 步骤3:平移回原位置
或者:
m.postRotate(degrees, px, py);
注意:preRotate和postRotate的旋转中心参数也是通过上述三个步骤实现的。
总结:
1. setRotate:重置矩阵,然后设置旋转(可以指定旋转中心)。
2. preRotate:在当前变换之前添加旋转(左乘),即新变换在原有变换之前执行。
3. postRotate:在当前变换之后添加旋转(右乘),即新变换在原有变换之后执行。
旋转中心(px, py)的作用:指定旋转操作围绕的点。
参考引用[4]和[5](虽然[5]是关于错切的,但原理相似)我们可以理解指定点的变换实现方式。
因此,在具体使用时:
- 如果你需要重新设置整个变换(丢弃之前的变换),使用setRotate。
- 如果你需要在当前变换的基础上再添加一个旋转,并且希望这个旋转先于当前变换执行,则用preRotate。
- 如果你需要在当前变换的基础上再添加一个旋转,并且希望这个旋转在当前变换之后执行,则用postRotate。
例如,如果我们要对一个图像先平移再旋转(以图像自身中心为旋转中心),可以:
Matrix matrix = new Matrix();
matrix.postTranslate(dx, dy); // 先平移
matrix.postRotate(degrees, centerX, centerY); // 再旋转(旋转中心是(centerX, centerY))
注意:这里使用postRotate,因为我们要先平移再旋转。如果我们使用preRotate,那么旋转会先于平移执行,导致不同的效果。
另外,注意旋转中心的选择:如果不指定,则默认是原点(0,0)。对于图像操作,通常我们以图像中心为旋转中心,所以需要指定。
下面我们通过一个代码示例来展示:
假设我们有一个Bitmap,我们想将它平移到(100,100),然后以图片中心为旋转中心旋转45度。
正确做法(使用postRotate):
Matrix matrix = new Matrix();
// 先设置平移
matrix.postTranslate(100, 100);
// 再设置旋转,旋转中心是图片中心(假设图片宽w,高h,则中心点(100+w/2, 100+h/2))
// 注意:因为平移已经将图片移到了(100,100)位置,所以旋转中心应该是相对于这个新位置的中心
float centerX = 100 + bitmap.getWidth()/2;
float centerY = 100 + bitmap.getHeight()/2;
matrix.postRotate(45, centerX, centerY);
错误做法(使用preRotate):
Matrix matrix = new Matrix();
// 先设置旋转(以目标中心为旋转中心,但此时图片还没有平移,所以中心点应该是(bitmap.getWidth()/2, bitmap.getHeight()/2))
matrix.preRotate(45, bitmap.getWidth()/2, bitmap.getHeight()/2);
// 再设置平移,这样会先旋转再平移,旋转中心是原图中心(在(0,0)位置的原图片的中心),然后平移到(100,100)
两种做法效果不同,需要根据需求选择。
所以,理解变换的顺序和旋转中心的选择非常重要。
相关问题:
1. 在Android Matrix中,set、pre和post方法在矩阵乘法上的区别是什么?
2. 如何用Matrix实现先缩放再旋转最后平移的复合变换?
3. Matrix中指定旋转中心(px,py)的变换原理是什么?
4. 使用Matrix进行图像变换时,如何避免因为变换顺序导致的错误效果?
5. 在自定义View中使用Matrix进行图形变换,有哪些常见的注意事项?</think>### Android Matrix setRotate vs preRotate vs postRotate 区别详解
在Android的`Matrix`类中,`setRotate`、`preRotate`和`postRotate`方法用于实现旋转变换,主要区别在于**变换重置行为**和**变换叠加顺序**。`px, py`参数用于指定旋转中心点(默认为坐标原点`(0,0)`)。
---
#### 1. **`setRotate` 方法**
- **作用**:重置当前Matrix的所有变换,然后设置旋转变换。
- **旋转中心**:
- `setRotate(float degrees)`:默认以坐标原点`(0,0)`为中心旋转。
- `setRotate(float degrees, float px, float py)`:以自定义点`(px, py)`为中心旋转。
- **底层原理**(以`(px, py)`为中心):
1. 平移:`T(-px, -py)`(将旋转中心移至原点)
2. 旋转:`R(degrees)`(绕原点旋转)
3. 平移:`T(px, py)`(移回原位置)[^4]
- **特性**:调用后**会清除Matrix之前的所有变换**,仅保留当前旋转[^2]。
---
#### 2. **`preRotate` 方法**
- **作用**:将旋转变换**前置叠加**到当前Matrix(新变换 × 原矩阵)。
- **旋转中心**:
- `preRotate(float degrees)`:绕原点旋转。
- `preRotate(float degrees, float px, float py)`:绕`(px, py)`旋转。
- **数学表示**:
若当前矩阵为 $M$,新旋转为 $R$,则结果为:
$$ M_{\text{new}} = R \times M $$
- **使用场景**:需先旋转再执行其他变换(如先旋转后平移)。
---
#### 3. **`postRotate` 方法**
- **作用**:将旋转变换**后置叠加**到当前Matrix(原矩阵 × 新变换)。
- **旋转中心**:
- `postRotate(float degrees)`:绕原点旋转。
- `postRotate(float degrees, float px, float py)`:绕`(px, py)`旋转。
- **数学表示**:
若当前矩阵为 $M$,新旋转为 $R$,则结果为:
$$ M_{\text{new}} = M \times R $$
- **使用场景**:需先执行其他变换再旋转(如先平移后旋转)。
---
### `px, py` 参数的作用
- **功能**:定义旋转变换的轴心点。
- **默认值**:未指定时,默认使用坐标原点 `(0,0)`。
- **实现原理**(以`postRotate(degrees, px, py)`为例):
```java
Matrix m = new Matrix();
m.postTranslate(-px, -py); // 移动到原点
m.postRotate(degrees); // 绕原点旋转
m.postTranslate(px, py); // 移回原位置
```
等价于直接调用 `postRotate(degrees, px, py)`[^4]。
---
### 关键区别总结
| **方法** | 变换重置 | 叠加顺序 | 适用场景 |
|----------------|----------|----------------|-----------------------------|
| `setRotate` | ✅ 会重置 | 无(覆盖旧变换) | 需清除历史变换,仅保留旋转 |
| `preRotate` | ❌ 保留 | 新变换 × 原矩阵 | 先旋转后执行其他变换(如缩放) |
| `postRotate` | ❌ 保留 | 原矩阵 × 新变换 | 先执行其他变换(如平移)后旋转 |
---
### 示例代码
```java
// 场景:先平移(dx,dy),再以点(px,py)为中心旋转45度
Matrix matrix = new Matrix();
matrix.postTranslate(dx, dy); // 步骤1:平移
matrix.postRotate(45, px, py); // 步骤2:绕(px,py)旋转
// 错误做法(旋转中心偏移):
matrix.setRotate(45, px, py); // 清除平移,仅保留旋转
matrix.postTranslate(dx, dy); // 旋转中心变为(px+dx, py+dy)
```
---
### 相关问题
1. 如何用`Matrix`实现「先缩放再旋转最后平移」的复合变换?
2. `preRotate`和`postRotate`在矩阵乘法中的顺序如何影响最终效果?
3. 为什么使用`setRotate`后调用`getValues()`会重置非平移参数[^2]?
4. 在自定义View中,`Matrix`的旋转中心`(px, py)`应如何根据视图尺寸动态计算?
5. `Matrix`的`pre`和`post`方法在性能上有差异吗?如何优化频繁变换的场景?
[^1]: Matrix在Android开发中,特别是一些高级UI的绘制中,做平移,旋转,缩放,错切操作很是适用。
[^2]: 当使用Matrix.java setXX方法时,其它变换会被重置。
[^3]: set方法都有与之对应的pre及post方法。
[^4]: 基于指定点旋转的实现分为三个步骤:平移-旋转-平移。
[^5]: setSkew方法演示了指定轴心点的变换原理。
阅读全文
相关推荐


















