自定义View(11)**在onDraw中使用矩阵Matrix
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的更多相关文章
- Android 自定义View及其在布局文件中的使用示例
前言: 尽管Android已经为我们提供了一套丰富的控件,如:Button,ImageView,TextView,EditText等众多控件,但是,有时候在项目开发过程中,还是需要开发者自定义一些需要 ...
- Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题
这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...
- Android中实现自定义View组件并使其能跟随鼠标移动
场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...
- Android自定义View学习笔记(一)
绘制基础 参考:HenCoder Android 开发进阶: 自定义 View 1-1 绘制基础 Paint详解 参考:HenCoder Android 开发进阶: 自定义 View 1-2 Pain ...
- Android之自定义View的实现
对于学习Android开发的小童鞋对于自定义View一定不会陌生,相信大家对它是又爱又恨,爱它可以跟随我们的心意设计出漂亮的效果:恨它想要完全流畅掌握,需要一定的功夫.对于初学者来说确实很不容易,网上 ...
- Android自定义View基础
自定义控件, 视频教程 http://www.jikexueyuan.com/course/1748.html 1. 编写自定义view 2. 加入逻辑线程 3. 提取和封装自定义view 4. 利用 ...
- 《Android进阶之光》--View体系与自定义View
No1: View的滑动 1)layout()方法的 public class CustomView extends View{ private int lastX; private int last ...
- Android自定义view控件
转载自: http://blog.163.com/ppy2790@126/blog/static/103242241201382210910473/ 开发自定义控件的步骤: 1.了解View的工作原理 ...
- 自定义View的实现流程
1.继承View组件,比如,LabelView继承了View 2.重写两个构造方法,比如,对于自定义View LabelView LabelView(Context context),如果该自 ...
随机推荐
- 问题解决:FFmpeg视频编解码库,无法解析的外部信号
在编译FFmpeg相关项目时.可能会出现: error LNK2019: 无法解析的外部符号 "int __cdecl avpicture_fill(struct AVPicture *,u ...
- curl 发送json请求
curl 发送json请求 这个是在cmd环境下的输入:注意{\"userName\":\"helo\",\"id\":1}中间不能有空格 ...
- 创立一个站点的前前后后(起因,域名,云平台,备案,CDN等等)(1)
起因 写完<完美软件开发:方法与逻辑>这书后,原本想继续写书的,可出来參加了些社区活动后,我发现我写的书大家评价还行.但事实上不太理解.而接下来想写的书更加抽象点.准备叫<管理的解析 ...
- react-redux 之 provider 和 connect
1.Provider 提供的是一个顶层容器的作用,实现store的上下文传递 2.connect 可以把state和dispatch绑定到react组件,使得组件可以访问到redux的数据 react ...
- unix时间戳(unix timestamp)与北京时间的互转方法
1.在linux bash下北京时间与unix时间戳互转: 获取unix timestamp: 命令:date "+%s" 输出:1372654714 获取北京时间: 命令:dat ...
- 【iOS系列】- UITableView的使用技巧
[iOS系列]- UITableView的使用 UITableView的常用属性 indexpath.row:行 indexpath.section:组 separatorColor:分割线的颜色 s ...
- 邮件:事务失败。 服务器响应为:DT:SPM 163 smtp
几年前我做的一个项目,日发邮件最高峰时几十万.自以为对邮件发送方面已经有了一定认识,所以近期机缘巧合之下,又有项目需要发送邮件,不禁自信满满,暗自庆幸能不手到擒来乎? 不想老革命遇到新问题.我原先的邮 ...
- 2016/05/13 Thinkphp 3.2.2 ①数据添加 ②收集表单数据入库操作 ③数据修改操作
①数据查询 add() 该方法返回被添加的新记录的主键id值 两种方式实现数据添加 数组方式数据添加 $goods = D(“Goods”); $arr = array(‘goods_name’=&g ...
- jQuery 1.x and 2.x , which is better?
1. jQuery 1.x和2.x的区别 或者可以说是jQuery 2.x有什么新特征? jQuery官方发布2.x原话 不再支持IE6/7/8,如果在IE9/10里只用“兼容性视图”模式也将会受到影 ...
- Apache POI组件操作Excel,制作报表(四)
Apache POI组件操作Excel,制作报表(四) 博客分类: 探索实践 ExcelApacheSpringMVCServlet 上一篇我们介绍了如何制作复杂报表的分析和设计,本篇结合S ...