1.代码示例

1.1 效果

  原图 : 其尺寸为162 x 251,示例中的红点是变形的锚点.

  

  变形之后:

  

  

1.2 代码

 package com.e.weixin.session.view;
import com.e.weixin.R; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.util.Log;
import android.view.View; public class MatrixView extends View {
private Paint paint;
private Bitmap bitmap;
private Matrix matrix;
private final int bitmapWidth, bitmapHeight;
private int padding = ; // 打印Matrix内数据
void showMatrix(){
// 下面的代码是为了查看matrix中的元素
float[] matrixValues = new float[];
matrix.getValues(matrixValues);
for (int i = ; i < ; ++i) {
String temp = new String();
for (int j = ; j < ; ++j) {
temp += matrixValues[ * i + j] + "\t";
}
Log.e("Matrix", temp);
}
} void drawFrameAndPivot(Canvas canvas, String txt, float column, float row, int pivotX, int pivotY){ matrix.reset(); float x = padding + (bitmapWidth + padding) * column ;
float y = padding + (bitmapHeight + padding) * row; Path frame = new Path(); // 画变形前的轮廓
frame.moveTo(x, y);
frame.lineTo(x + bitmapWidth, y);
frame.lineTo(x + bitmapWidth, y + bitmapHeight);
frame.lineTo(x, y + bitmapHeight);
frame.lineTo(x, y); matrix.postTranslate(x, y); paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE); canvas.drawText(txt, x, y + bitmapHeight + paint.getTextSize(), paint); canvas.drawPath(frame, paint);
// 画中心点(经过上面移动后),
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x + pivotX, y + pivotY, , paint); } // 1. 平移
void translateMatrix(Canvas canvas){
// 重置matrix
matrix.reset(); // 开始平移
matrix.postTranslate(padding + (bitmapWidth + padding) * 1.5f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "平移", , , , ); }
// 2. 缩放
void scaleMatrix(Canvas canvas){
matrix.reset(); // 开始缩放,第1个参数是在x轴的缩放比例,第2个是y轴。默认以(0,0)点
matrix.setScale(2.5f, 1.0f); // 打印缩放前Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 2.5f,padding + ( bitmapHeight + padding ) * ); // 打印缩放后Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "缩放(默认中心点是0,0)", 2.5f, , , );
}
// 3. 旋转(默认点)
void rotateMatrix2(Canvas canvas){
matrix.reset(); // 开始旋转,有旋转和平两个效果时,要先旋转再平移。
// 不指定旋转点时默认是图形的左,上点
matrix.setRotate(45f, , ); // 移动位置
matrix.postTranslate(padding + (bitmapWidth + padding) * 1f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "旋转(默认中心点是0,0)", 1f, , , );
} // 4. 旋转(指定一个旋转角度和中心点)
void rotateMatrix1(Canvas canvas){
matrix.reset(); // 开始旋转,有旋转和平两个效果时,要先旋转再平移。
// 第1个参数是角度,第2,3个参数是旋转中心点的坐标,它们最好在bitmap的宽高之内取值。
// 中心点的理解:
// 好比啬墙上有一幅画,右手食指按住画不动,然后左手拖动画做变形,旋转,绽放等动作。
// 其中右手食指按一点就是中心点,它一般在画内,但是也可以在画外。
matrix.setRotate(45f, bitmapWidth / , bitmapHeight / ); // 移动位置
matrix.postTranslate(padding + (bitmapWidth + padding) * 3f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
paint.setColor(Color.BLACK);
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "旋转(指定中心点)", , , bitmapWidth / , bitmapHeight / ); }
// 5. 错切 - 水平
void hSkewMatrix(Canvas canvas){
matrix.reset(); // 打印Matrix变形前的数据
showMatrix(); matrix.setSkew(0.5f, 0f); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 0f, padding + (bitmapHeight + padding) * ); // 打印Matrix变形后的数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "错切 - 水平", , , , );
}
// 6. 错切 - 垂直
void vSkewMatrix(Canvas canvas){
matrix.reset(); // 开始垂直错切,
matrix.setSkew(0f, 0.5f); // 打印Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 1.6f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "错切 - 垂直", 1.6f, , , );
}
// 7. 错切 - 水平 + 垂直
void hvSkewMatrix(Canvas canvas){
matrix.reset(); // 开始 水平 + 垂直 错切
matrix.setSkew(0.5f, 0.5f); // 打印Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 3f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "错切 - 水平 + 垂直", 3f, , , );
} // 8. 对称 - 垂直
void vDcMatrix(Canvas canvas){
matrix.reset(); // 准备对称
float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
matrix.setValues(matrix_values); // 打印Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 0.7f, padding + (bitmapHeight + padding) * ); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "垂直对称", 0.7f, , , );
} // 9. 对称 (水平对称)
void hDCMatrix(Canvas canvas){
matrix.reset(); // 准备对称
float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
matrix.setValues(matrix_values); // 打印Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 1.6f, padding + (bitmapHeight + padding) * 4f); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "水平对称", 1.6f, 4f, , );
} // 10. 对称(对称轴为直线y = x)
void dczMatrix(Canvas canvas){
matrix.reset(); // 准备对称
float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
matrix.setValues(matrix_values); // 打印Matrix内数据
showMatrix(); // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
matrix.postTranslate(padding + (bitmapWidth + padding) * 3.6f, padding + (bitmapHeight + padding) * 4f); // 打印Matrix内数据
showMatrix(); // 画出变换后的图像
canvas.drawBitmap(bitmap, matrix, paint); // 画出变形前的轮廓
drawFrameAndPivot(canvas, "对称轴为直线y = x", 3.6f, 4f, , );
} //==============
@Override
protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 画出原图像
canvas.drawBitmap(bitmap, matrix, paint);
drawFrameAndPivot(canvas, "原图", , , , ); // 1. 平移
translateMatrix(canvas); // 2. 缩放
scaleMatrix(canvas); // 3. 旋转(默认中心点)
rotateMatrix2(canvas); // 4. 旋转(指定一个旋转角度和中心点)
rotateMatrix1(canvas); // 5. 错切 - 水平
hSkewMatrix(canvas); // 6. 错切 - 垂直
vSkewMatrix(canvas); // 7. 错切 - 水平 + 垂直
hvSkewMatrix(canvas); // 8. 对称 - 垂直
vDcMatrix(canvas); // 9. 对称 (水平对称)
hDCMatrix(canvas); // 10. 对称(对称轴为直线y = x)
dczMatrix(canvas);
} public MatrixView(Context context) {
super(context); paint = new Paint(Paint.ANTI_ALIAS_FLAG);
PathEffect effects = new DashPathEffect(new float[]{,,,,, }, );
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(effects); matrix = new Matrix();
matrix.postTranslate(padding,padding);//默认位置
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
bitmapWidth = bitmap.getWidth();
bitmapHeight = bitmap.getHeight(); } @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Try for a bitmapWidth based on our minimum
int w = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();//最小宽度 if(w == ) w = ;
//计算最佳值,在其中解析了widthMeasureSpec,这时如果w大于widthMeasureSpec中的值,则选widthMeasureSpec
w = resolveSizeAndState(w, widthMeasureSpec, ); // Whatever the w ends up being, ask for a bitmapHeight that would let the pie
// get as big as it can
int h = getSuggestedMinimumHeight() + getPaddingBottom() + getPaddingTop();//最小高度 if(h == ) h = ;//如果给出的建议是0,可以手动设置一个期望值。单位是像素。 h = resolveSizeAndState(h, heightMeasureSpec, );//计算最佳值,在其中解析了heightMeasureSpec //将量算的结果保存到View的成员变量mMeasuredWidth 和mMeasuredHeight中。
setMeasuredDimension(w, h); // 量算完成之后,View的父控件就可以通过调用
// getMeasuredWidth、getMeasuredState、getMeasuredWidthAndState
// 这三个方法获取View的量算结果。
} }

2. 关于pre,set,post变形方式的区别

  pre是在队列最前面插入,post是在队列最后面追加,而set先清空队列在添加。

2.1 示例1

     matrix.preScale(2f,1f);
matrix.preTranslate(5f, 0f);
matrix.postScale(0.2f, 1f);
matrix.postTranslate(0.5f, 0f);

  执行顺序:translate(5, 0) -> scale(2f, 1f) -> scale(0.2f, 1f) -> translate(0.5f, 0f)

2.2 示例2

     matrix.postTranslate(2f, 0f);
matrix.preScale(0.2f, 1f);
matrix.setScale(1f, 1f);
matrix.postScale(5f, 1f);
matrix.preTranslate(0.5f, 0f);

  执行顺序:translate(0.5f, 0f) -> scale(1f, 1f) -> scale(5f, 1)  执行了setScale后,前面两句设置的矩阵变化就不起作用了。(matrix.postTranslate(2f, 0f);  matrix.preScale(0.2f, 1f); 不起作用)

  注意其中的set系列,清除前面的变形.

3.变形时改变默认锚点

  变形的默认锚点是左上(0,0),通常缩放或旋转等等变形操作希望改变默认的锚点,而调用view.setPivotX((float) viewWidth / 2); setPivotY或者在xml中 android:transformPivotX="200dp"不起作用,这时可用相应的系列变形方法如,

  • postScale(float sx, float sy, float px, float py)
  • postRotate(float degrees, float px, float py)
  • setScale(float sx, float sy, float px, float py)
  • setSkew(float kx, float ky, float px, float py)
  • 等等...后面带有float px,float py的方法.

  上面系列方法中的float px,float py 就是变形锚点.

4.变形的数学原理

  

  矩阵中的MSCALE用于处理缩放变换,MSKEW用于处理错切变换,MTRANS用于处理平移变换,MPERSP用于处理透视变换。

  http://www.cnblogs.com/qiengo/archive/2012/06/30/2570874.html

自定义View(11)**在onDraw中使用矩阵Matrix的更多相关文章

  1. Android 自定义View及其在布局文件中的使用示例

    前言: 尽管Android已经为我们提供了一套丰富的控件,如:Button,ImageView,TextView,EditText等众多控件,但是,有时候在项目开发过程中,还是需要开发者自定义一些需要 ...

  2. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  3. Android中实现自定义View组件并使其能跟随鼠标移动

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...

  4. Android自定义View学习笔记(一)

    绘制基础 参考:HenCoder Android 开发进阶: 自定义 View 1-1 绘制基础 Paint详解 参考:HenCoder Android 开发进阶: 自定义 View 1-2 Pain ...

  5. Android之自定义View的实现

    对于学习Android开发的小童鞋对于自定义View一定不会陌生,相信大家对它是又爱又恨,爱它可以跟随我们的心意设计出漂亮的效果:恨它想要完全流畅掌握,需要一定的功夫.对于初学者来说确实很不容易,网上 ...

  6. Android自定义View基础

    自定义控件, 视频教程 http://www.jikexueyuan.com/course/1748.html 1. 编写自定义view 2. 加入逻辑线程 3. 提取和封装自定义view 4. 利用 ...

  7. 《Android进阶之光》--View体系与自定义View

    No1: View的滑动 1)layout()方法的 public class CustomView extends View{ private int lastX; private int last ...

  8. Android自定义view控件

    转载自: http://blog.163.com/ppy2790@126/blog/static/103242241201382210910473/ 开发自定义控件的步骤: 1.了解View的工作原理 ...

  9. 自定义View的实现流程

    1.继承View组件,比如,LabelView继承了View   2.重写两个构造方法,比如,对于自定义View LabelView   LabelView(Context context),如果该自 ...

随机推荐

  1. 同步定制 Unity团队 程序的C#文件模板

    孙广东   2015.7.30 就是把程序制定好的模板(不论什么人能够更改并同步git)放到,unity项目的Editor 目录下, 当程序新建一个C#脚本后就是这个模板了. "81-C# ...

  2. Android中个人推崇的数据库使用方式

    手机应用开发中常常会使用到数据库存储一些资料或者进行数据缓存,android中为我们提供了一个轻量的数据库.在上层进行了一层封装,同一时候还为我们提供了ContentProvider的框架.方便我们进 ...

  3. OUTPUT 子句

    除了修改数据以外,一般不会希望修改语句后再做其他事情.也就是说,一般不会希望修改语句能够返回任何输出.然而,在有些场合下,能够从修改过的行中返回数据,这个功能可能也有一定的用处. 例如,考虑UPDAT ...

  4. 【iOS系列】-oc中的集合类

    OC中的集合有:NSArray 数组 NSDictionary 字典 NSSet 集合 第一:NSArrary 1.1:集合的基本方法 //1.创建集合 //NSArray 是不可变数组,一旦创建完成 ...

  5. 2016/04/26 权限 数据库mydb2 五个表 分别是 1,用户 2,角色 3,权限 4,用户对应的角色 5,角色对应的权限

    权限:   1,后台分配角色     角色对应权限    2,各用户通过登录页面登录    查看到各自的权限 五个页面   加引入一个jquery-1.11.2.min.js 1,guanli.php ...

  6. HTML的DOM和浏览器的BOM

    DOM和BOM的区别 HTML DOM 的 document 是 BOM 的 window 对象的属性之一: 通过可编程的对象模型,JavaScript 获得了足够的能力来创建动态的 HTML. Ja ...

  7. Speech Recognition Grammar Specification Version 1.0 JavaScript TTS 文本发音

    Speech Recognition Grammar Specification Version 1.0 https://www.w3.org/TR/speech-grammar/ W3C Recom ...

  8. cgi fastcgi wsgi nginx python Dispatching TurboGears Python via FCGI

    https://www.nginx.com/resources/wiki/start/topics/examples/simplepythonfcgi/ 117 2018-06-28 19:56:42 ...

  9. Ubuntu下Jupyter Notebook的安装

    pip install --upgrade pip //更新pip pip install jupyter sudo apt install jupyter-notebook 运行 jupyter-n ...

  10. javascript 省、市、地县三级联动

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><HTML><HEAD&g ...