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. 全文搜索引擎 Elasticsearch 安装

    全文搜索引擎 Elasticsearch 安装 学习了:http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html 拼音:https://www ...

  2. HDU3926Hand in Hand(搜索 或 并查集)

    Problem Description In order to get rid of Conan, Kaitou KID disguises himself as a teacher in the k ...

  3. C# Json反序列化 数据协定类型 无法反序列化 由于未找到必需的数据成员

    背景今天在使用:C# Json 序列化与反序列化 反序列化的时候出现了以下的错误信息. System.Runtime.Serialization.SerializationException: 数据协 ...

  4. Android开发pool解析xml

    xml在开发中的作用不可小觑,很多时候我们都要用到这种文件,所以学习它的解析方式很是必要. 我们都知道java中xml的解析有:dom,SAX,但是Android下我们使用pool解析,是更为方便,而 ...

  5. “千千静听”滚动标题栏,非常简单!(时间器控制窗口标题栏文字,然后赋值给Application.Title)

    记得曾写过类似功能,但由于对Delphi数据类型不清楚,要花不少代码去处理中文被切半而出现乱码的尴尬.后来知道只需把字符串定义成 WideString 即可解决半个中文的问题了. 实现过程:不停地剪切 ...

  6. 通过fsharp 使用Enterprise Library Unity 3 - 三种拦截模式的探索

    这篇就三种拦截模式进行一下探索. 特性总结   类型 特点 其它 InterfaceInterceptor Innstance 仅单接口 类内部函数互相引用无法引起拦截行为 TransparentPr ...

  7. ABAP WEBRFC

    通过WEBRFC实现在网页下载SMW0上传的文件 FUNCTION zhr_download_test. *"---------------------------------------- ...

  8. Jackson 框架的高阶应用

    Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架.Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson ...

  9. 使用C#开发HTTP服务器系列之访问主页

    各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://qinyuanpei.com.在这个系列文章的第一篇中,我们着重认识和了解了HTTP协议,并在此基础上实现了一个可交互的W ...

  10. 【FFT初识】

      FFT在用于解决多项式乘法A*B(A和B为多项式,形如a0+a1*x^1+a2*x^2....)的时候,通俗地解释就是: 原理:先根据各自的系数各自转化为对应的向量(O(nlogn)),然后向量相 ...