Canvas 类中 drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) 方法中有个参数类型是 Matrix,从字面上理解是矩阵的意思,而实际上它也确实是个 3x3 的矩阵。Matrix 在 Android 中的主要作用是图像变换,如平移、旋转、缩放、扭曲等。 关于图像如何通过矩阵进行变化可参考这篇文章
Matrix 内部通过维护一个 float[9] 的数组来构成 3x3 矩阵的形式,而实际上所有的变换方法说到底就是通过更改数组中某个或某几个位置的数值。Matrix 提供了 setValues() 和 getValues() 方法来操作数组。
显然这两个方法使用起来很不方便,如果只有这样,那 Matrix 估计就不会有人使用了。Google 轻易不会辜负我们的信任,Matrix 提供了若干简单易用的变换方法和映射方法供开发者使用。
Matrix 提供了 Translate(平移)、Scale(缩放)、Rotate(旋转)、Skew(扭曲) 四中变换操作,这四种操作实质上是调用了 setValues() 方法来设置矩阵数组来达到变换效果。
除 Translate(平移)外,Scale(缩放)、Rotate(旋转)、Skew(扭曲)都可以围绕一个中心点来进行,如果不指定,在默认情况下是围绕 (0, 0) 来进行相应的变换的。
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);
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);
下面是常用变换的例子,除了变换参数,其他代码也有一些差异,可以多认识几种实现方法
- /**
- * 图片移动
- */
- 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 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 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);
- }
- /**
- * 倾斜图片
- */
- 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>
参考:
来源: http://www.bubuko.com/infodetail-1972176.html