Canvas类中drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法中有个參数类型是Matrix。从字面上理解是矩阵的意思,而实际上它也确实是个3x3的矩阵。Matrix在Android中的主要作用是图像变换,如平移、旋转、缩放、扭曲等。 关于图像怎样通过矩阵进行变化可參考这篇文章图像处理—关于像素坐标矩阵变换(平移,旋转。缩放。错切)
Matrix内部通过维护一个float[9]的数组来构成3x3矩阵的形式,而实际上全部的变换方法说究竟就是通过更改数组中某个或某几个位置的数值。
Matrix提供了setValues()和getValues()方法来操作数组。
显然这两个方法使用起来非常不方便。假设仅仅有这样,那Matrix预计就不会有人使用了。Google轻易不会辜负我们的信任。Matrix提供了若干简单易用的变换方法和映射方法供开发人员使用。
Matrix变换方法
1、Translate(平移)、Scale(缩放)、Rotate(旋转)、Skew(扭曲)
Matrix提供了Translate(平移)、Scale(缩放)、Rotate(旋转)、Skew(扭曲)四中变换操作,这四种操作实质上是调用了setValues()方法来设置矩阵数组来达到变换效果。
除Translate(平移)外。Scale(缩放)、Rotate(旋转)、Skew(扭曲)都能够环绕一个中心点来进行,假设不指定。在默认情况下是环绕(0, 0)来进行对应的变换的。
Translate操作中
- 在x轴上使用正数进行平移将向右移动图像,而使用负数将向左移动图像。
- 在y轴上使用正数进行平移将向下移动图像,而使用负数将向上移动图像。
Scale操作中,
- 在x轴上使用正数进行缩放将在中心点的右边缩放图像,而使用负数将在中心点的左边缩放图像。
- 在y轴上使用正数进行缩放将在中心点的下边缩放图像,而使用负数将在中心点的上边缩放图像。
2、pre、set、post
Matrix提供的四种操作,每一种都有pre、set、post三种形式。原因是矩阵乘法不满足乘法交换律,因此左乘还是右乘终于的效果都不一样。
左乘或者右乘是针对变换矩阵的,得到终于的变换矩阵后,与图像矩阵想成,图像矩阵在右边。
pre方法表示矩阵左乘,比如:变换矩阵为A,原始矩阵为B。pre方法的含义即是A*B
post方法表示矩阵右乘,比如:变换矩阵为A,原始矩阵为B,post方法的含义即是B*A
我们能够把Matrix变换想象成一个队列,队列里面包括了若干个变换操作,队列中每一个操作依照先后顺序操作变换目标完毕变换。pre相当于向队首添加一个操作,post相当于向队尾添加一个操作,set相当于清空当前队列又一次设置。
///这段代码仅仅有translate(100, 100)生效。由于第二个set会把之前队列中的操作清除。
Matrix m = new Matrix();
m.setRotate(100);
m.setTranslate(100, 100);
//这段代码先运行translate(100, 100)。后运行rotate(100)
Matrix m = new Matrix();
m.setTranslate(100, 100);
m.postRotate(100);
///这段代码先运行rotate(100)。后运行translate(100, 100)
Matrix m = new Matrix();
m.setTranslate(100, 100);
m.preRotate(100);
///这段代码的运行顺序: translate(100f, 100f) -> scale(2f, 2f) -> scale(0.5f, 0.5f) -> translate(50f, 50f)
Matrix m = new Matrix();
m.preScale(2f, 2f);
m.preTranslate(100f, 100f);
m.postScale(0.5f, 0.5f);
m.postTranslate(50f, 50f);
//这段代码的运行顺序:translate(50f, 50f) -> scale(0.8f, 0.8f) -> scale(3f, 3f)
Matrix m = new Matrix();
m.postTranslate(100, 100);
m.preScale(0.5f, 0.5f);
m.setScale(0.8f, 0.8f);
m.postScale(3f, 3f);
m.preTranslate(50f, 50f);
3、Matrix 映射方法
Matrix提供了mapPoints(),mapRects(),mapVectors()等映射方法。用来获取经Matrix映射后的值
//这段代码的作用是获取经过平移后该bitmap四个点的坐标
Matrix m = new Matrix();
m.postTranslate(100f, 100f);
float[] src = {
0, 0,
0, bitmap.getHeight(),
bitmap.getWidth(), 0,
bitmap.getWidth(), bitmap.getHeight()
};
float[] dst = new float[8];
m.mapPoints(dst, src);
经常使用的矩阵变化方法
以下是经常使用变换的样例,除了变换參数。其它代码也有一些差异,能够多认识几种实现方法
平移Translate
/**
* 图片移动
*/
protected void bitmapTranslate(float dx, float dy) {
// 须要依据移动的距离来创建图片的拷贝图大小
Bitmap afterBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() + dx),
(int) (baseBitmap.getHeight() + dy), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 设置移动的距离
matrix.setTranslate(dx, dy);
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
缩放Scale
/**
* 缩放图片
*/
protected void bitmapScale(float x, float y) {
// 由于要将图片放大,所以要依据放大的尺寸又一次创建Bitmap
Bitmap afterBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() * x),
(int) (baseBitmap.getHeight() * y), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.setScale(x, y);
// 依据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
注意观察以下两个镜像的实现方法。
X轴镜像
/**
* x轴镜像
*/
protected void bitmapXMirror() {
// 由于要将图片放大,所以要依据放大的尺寸又一次创建Bitmap
Bitmap afterBitmap = Bitmap.createBitmap(
baseBitmap.getWidth() ,
baseBitmap.getHeight() , baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.postScale(-1, 1);
matrix.postTranslate(baseBitmap.getWidth(), 0);
// 依据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
Y轴镜像
/**
* y轴镜像
*/
protected void bitmapYMirror() {
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.postScale(1, -1);
//依据变换矩阵,绘制新的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true);
iv_after.setImageBitmap(afterBitmap);
}
旋转Rotate
/**
* 图片旋转
*/
protected void bitmapRotate(float degrees) {
// 创建一个和原图一样大小的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
baseBitmap.getHeight(), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 依据原图的中心位置旋转
matrix.setRotate(degrees, baseBitmap.getWidth() / 2,
baseBitmap.getHeight() / 2);
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
扭曲Skew
/**
* 倾斜图片
*/
protected void bitmapSkew(float dx, float dy) {
// 依据图片的倾斜比例。计算变换后图片的大小,
Matrix matrix = new Matrix();
// 设置图片倾斜的比例
matrix.setSkew(dx, dy);
//依据变换矩阵,绘制新的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true);
iv_after.setImageBitmap(afterBitmap);
}
源代码
完整的Demo
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button btn_scale, btn_rotate, btn_translate, btn_skew,btn_XMirror,btn_YMirror;
private ImageView iv_base, iv_after;
private Bitmap baseBitmap;
private Paint paint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_scale = (Button) findViewById(R.id.btn_scale);
btn_rotate = (Button) findViewById(R.id.btn_rotate);
btn_translate = (Button) findViewById(R.id.btn_translate);
btn_skew = (Button) findViewById(R.id.btn_skew);
btn_XMirror = (Button) findViewById(R.id.btn_x_mirror);
btn_YMirror = (Button) findViewById(R.id.btn_y_mirror);
btn_scale.setOnClickListener(click);
btn_rotate.setOnClickListener(click);
btn_translate.setOnClickListener(click);
btn_skew.setOnClickListener(click);
btn_XMirror.setOnClickListener(click);
btn_YMirror.setOnClickListener(click);
iv_base = (ImageView) findViewById(R.id.iv_base);
iv_after = (ImageView) findViewById(R.id.iv_after);
baseBitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher);
iv_base.setImageBitmap(baseBitmap);
// 设置画笔,消除锯齿
paint = new Paint();
paint.setAntiAlias(true);
}
private View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_scale:
bitmapScale(2.0f, 4.0f);
break;
case R.id.btn_rotate:
bitmapRotate(180);
break;
case R.id.btn_translate:
bitmapTranslate(20f, 20f);
break;
case R.id.btn_skew:
bitmapSkew(0.2f, 0.4f);
break;
case R.id.btn_x_mirror:
bitmapXMirror();
break;
case R.id.btn_y_mirror:
bitmapYMirror();
break;
default:
break;
}
}
};
/**
* 缩放图片
*/
protected void bitmapScale(float x, float y) {
// 由于要将图片放大。所以要依据放大的尺寸又一次创建Bitmap
Bitmap afterBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() * x),
(int) (baseBitmap.getHeight() * y), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.setScale(x, y);
// 依据缩放比例。把图片draw到Canvas上
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
/**
* x轴镜像
*/
protected void bitmapXMirror() {
// 由于要将图片放大,所以要依据放大的尺寸又一次创建Bitmap
Bitmap afterBitmap = Bitmap.createBitmap(
baseBitmap.getWidth() ,
baseBitmap.getHeight() , baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.postScale(-1, 1);
matrix.postTranslate(baseBitmap.getWidth(), 0);
// 依据缩放比例,把图片draw到Canvas上
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
/**
* y轴镜像
*/
protected void bitmapYMirror() {
// 初始化Matrix对象
Matrix matrix = new Matrix();
// 依据传入的參数设置缩放比例
matrix.postScale(1, -1);
//依据变换矩阵。绘制新的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true);
iv_after.setImageBitmap(afterBitmap);
}
/**
* 倾斜图片
*/
protected void bitmapSkew(float dx, float dy) {
// 依据图片的倾斜比例。计算变换后图片的大小,
Matrix matrix = new Matrix();
// 设置图片倾斜的比例
matrix.setSkew(dx, dy);
//依据变换矩阵,绘制新的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true);
iv_after.setImageBitmap(afterBitmap);
}
/**
* 图片移动
*/
protected void bitmapTranslate(float dx, float dy) {
// 须要依据移动的距离来创建图片的拷贝图大小
Bitmap afterBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() + dx),
(int) (baseBitmap.getHeight() + dy), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 设置移动的距离
matrix.setTranslate(dx, dy);
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
/**
* 图片旋转
*/
protected void bitmapRotate(float degrees) {
// 创建一个和原图一样大小的图片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
baseBitmap.getHeight(), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 依据原图的中心位置旋转
matrix.setRotate(degrees, baseBitmap.getWidth() / 2,
baseBitmap.getHeight() / 2);
canvas.drawBitmap(baseBitmap, matrix, paint);
iv_after.setImageBitmap(afterBitmap);
}
}
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_scale"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:text="缩放" />
<Button
android:id="@+id/btn_rotate"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="旋转" />
<Button
android:id="@+id/btn_translate"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="平移" />
<Button
android:id="@+id/btn_skew"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="倾斜" />
<Button
android:id="@+id/btn_x_mirror"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="X轴镜像" />
<Button
android:id="@+id/btn_y_mirror"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Y轴镜像" />
</LinearLayout>
<!-- 原始图片 -->
<ImageView
android:id="@+id/iv_base"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- 处理之后的图片 -->
<ImageView
android:id="@+id/iv_after"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
參考:
Android Matrix
Android—Matrix类的使用
Android中Matrix的pre post set方法理解
Android–Matrix图片变换处理
android bitmap翻转180,镜像的简单实现方法
图像处理—关于像素坐标矩阵变换(平移,旋转。缩放。错切)