自定义View(11)**在onDraw中使用矩阵Matrix

1.代码示例

1.1 效果

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

  

  变形之后:

  

 

  

1.2 代码

  1 package com.e.weixin.session.view;
  2 import com.e.weixin.R;
  3 
  4 
  5 import android.content.Context;
  6 import android.graphics.Bitmap;
  7 import android.graphics.BitmapFactory;
  8 import android.graphics.Canvas;
  9 import android.graphics.Color;
 10 import android.graphics.DashPathEffect;
 11 import android.graphics.Matrix;
 12 import android.graphics.Paint;
 13 import android.graphics.Path;
 14 import android.graphics.PathEffect;
 15 import android.util.Log;
 16 import android.view.View;
 17 
 18 public class MatrixView extends View {
 19     private Paint paint;
 20     private Bitmap bitmap;
 21     private Matrix matrix;
 22     private final int bitmapWidth, bitmapHeight;
 23     private int padding = 40;
 24 
 25     // 打印Matrix内数据
 26     void showMatrix(){
 27         // 下面的代码是为了查看matrix中的元素
 28         float[] matrixValues = new float[9];
 29         matrix.getValues(matrixValues);
 30         for (int i = 0; i < 3; ++i) {
 31             String temp = new String();
 32             for (int j = 0; j < 3; ++j) {
 33                 temp += matrixValues[3 * i + j] + "\t";
 34             }
 35             Log.e("Matrix", temp);
 36         }
 37     }
 38 
 39     void drawFrameAndPivot(Canvas canvas, String txt, float column, float row, int pivotX, int pivotY){
 40 
 41         matrix.reset();
 42 
 43         float x = padding + (bitmapWidth + padding) * column ;
 44         float y = padding + (bitmapHeight + padding) * row;
 45 
 46         Path frame = new Path();
 47 
 48         // 画变形前的轮廓
 49         frame.moveTo(x, y);
 50         frame.lineTo(x + bitmapWidth, y);
 51         frame.lineTo(x + bitmapWidth, y + bitmapHeight);
 52         frame.lineTo(x, y + bitmapHeight);
 53         frame.lineTo(x, y);
 54 
 55         matrix.postTranslate(x, y);
 56 
 57         paint.setColor(Color.BLACK);
 58         paint.setStyle(Paint.Style.STROKE);
 59 
 60         canvas.drawText(txt, x, y + bitmapHeight + paint.getTextSize(), paint);
 61 
 62         canvas.drawPath(frame, paint);
 63         // 画中心点(经过上面移动后),
 64         paint.setColor(Color.RED);
 65         paint.setStyle(Paint.Style.FILL);
 66         canvas.drawCircle(x + pivotX, y + pivotY, 5, paint);
 67 
 68     }
 69 
 70 
 71     // 1. 平移
 72     void translateMatrix(Canvas canvas){
 73         // 重置matrix
 74         matrix.reset();
 75 
 76         // 开始平移
 77         matrix.postTranslate(padding + (bitmapWidth + padding) * 1.5f, padding + (bitmapHeight + padding) * 0);
 78 
 79         // 打印Matrix内数据
 80         showMatrix();
 81 
 82         // 画出变换后的图像
 83         canvas.drawBitmap(bitmap, matrix, paint);
 84 
 85         // 画出变形前的轮廓
 86         drawFrameAndPivot(canvas, "平移", 1, 0, 0, 0);
 87 
 88     }
 89     // 2. 缩放
 90     void scaleMatrix(Canvas canvas){
 91         matrix.reset();
 92 
 93         // 开始缩放,第1个参数是在x轴的缩放比例,第2个是y轴。默认以(0,0)点
 94         matrix.setScale(2.5f, 1.0f);
 95 
 96         // 打印缩放前Matrix内数据
 97         showMatrix();
 98 
 99         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
100         matrix.postTranslate(padding + (bitmapWidth + padding) * 2.5f,padding + ( bitmapHeight + padding ) * 0);
101 
102         // 打印缩放后Matrix内数据
103         showMatrix();
104 
105         // 画出变换后的图像
106         canvas.drawBitmap(bitmap, matrix, paint);
107 
108         // 画出变形前的轮廓
109         drawFrameAndPivot(canvas, "缩放(默认中心点是0,0)", 2.5f, 0, 0, 0);
110     }
111     // 3. 旋转(默认点)
112     void rotateMatrix2(Canvas canvas){
113         matrix.reset();
114 
115         // 开始旋转,有旋转和平两个效果时,要先旋转再平移。
116         // 不指定旋转点时默认是图形的左,上点
117         matrix.setRotate(45f, 0, 0);
118 
119         // 移动位置
120         matrix.postTranslate(padding + (bitmapWidth + padding) * 1f, padding + (bitmapHeight + padding) * 1);
121 
122         // 打印Matrix内数据
123         showMatrix();
124 
125         // 画出变换后的图像
126         canvas.drawBitmap(bitmap, matrix, paint);
127 
128         // 画出变形前的轮廓
129         drawFrameAndPivot(canvas, "旋转(默认中心点是0,0)", 1f, 1, 0, 0);
130     }
131 
132     // 4. 旋转(指定一个旋转角度和中心点)
133     void rotateMatrix1(Canvas canvas){
134         matrix.reset();
135 
136         // 开始旋转,有旋转和平两个效果时,要先旋转再平移。
137         // 第1个参数是角度,第2,3个参数是旋转中心点的坐标,它们最好在bitmap的宽高之内取值。
138         // 中心点的理解:
139         // 好比啬墙上有一幅画,右手食指按住画不动,然后左手拖动画做变形,旋转,绽放等动作。
140         // 其中右手食指按一点就是中心点,它一般在画内,但是也可以在画外。
141         matrix.setRotate(45f, bitmapWidth / 2, bitmapHeight / 2);
142 
143         // 移动位置
144         matrix.postTranslate(padding + (bitmapWidth + padding) * 3f, padding + (bitmapHeight + padding) * 1);
145 
146         // 打印Matrix内数据
147         showMatrix();
148 
149         // 画出变换后的图像
150         paint.setColor(Color.BLACK);
151         canvas.drawBitmap(bitmap, matrix, paint);
152 
153         // 画出变形前的轮廓
154         drawFrameAndPivot(canvas, "旋转(指定中心点)", 3, 1, bitmapWidth / 2, bitmapHeight / 2);
155 
156     }
157     // 5. 错切 - 水平
158     void hSkewMatrix(Canvas canvas){
159         matrix.reset();
160 
161         // 打印Matrix变形前的数据
162         showMatrix();
163 
164         matrix.setSkew(0.5f, 0f);
165 
166         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
167         matrix.postTranslate(padding + (bitmapWidth + padding) * 0f, padding + (bitmapHeight + padding) * 2);
168 
169         // 打印Matrix变形后的数据
170         showMatrix();
171 
172         // 画出变换后的图像
173         canvas.drawBitmap(bitmap, matrix, paint);
174 
175         // 画出变形前的轮廓
176         drawFrameAndPivot(canvas, "错切 - 水平", 0, 2, 0, 0);
177     }
178     // 6. 错切 - 垂直
179     void vSkewMatrix(Canvas canvas){
180         matrix.reset();
181 
182         // 开始垂直错切,
183         matrix.setSkew(0f, 0.5f);
184 
185         // 打印Matrix内数据
186         showMatrix();
187 
188         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
189         matrix.postTranslate(padding + (bitmapWidth + padding) * 1.6f, padding + (bitmapHeight + padding) * 2);
190 
191         // 打印Matrix内数据
192         showMatrix();
193 
194         // 画出变换后的图像
195         canvas.drawBitmap(bitmap, matrix, paint);
196 
197         // 画出变形前的轮廓
198         drawFrameAndPivot(canvas, "错切 - 垂直", 1.6f, 2, 0, 0);
199     }
200     // 7. 错切 - 水平 + 垂直
201     void hvSkewMatrix(Canvas canvas){
202         matrix.reset();
203 
204         // 开始 水平 + 垂直 错切
205         matrix.setSkew(0.5f, 0.5f);
206 
207         // 打印Matrix内数据
208         showMatrix();
209 
210         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
211         matrix.postTranslate(padding + (bitmapWidth + padding) * 3f, padding + (bitmapHeight + padding) * 2);
212 
213         // 打印Matrix内数据
214         showMatrix();
215 
216         // 画出变换后的图像
217         canvas.drawBitmap(bitmap, matrix, paint);
218 
219         // 画出变形前的轮廓
220         drawFrameAndPivot(canvas, "错切 - 水平 + 垂直", 3f, 2, 0, 0);
221     }
222 
223     // 8. 对称 - 垂直
224     void vDcMatrix(Canvas canvas){
225         matrix.reset();
226 
227         // 准备对称
228         float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
229         matrix.setValues(matrix_values);
230 
231         // 打印Matrix内数据
232         showMatrix();
233 
234         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
235         matrix.postTranslate(padding + (bitmapWidth + padding) * 0.7f, padding + (bitmapHeight + padding) * 4);
236 
237         // 打印Matrix内数据
238         showMatrix();
239 
240         // 画出变换后的图像
241         canvas.drawBitmap(bitmap, matrix, paint);
242 
243         // 画出变形前的轮廓
244         drawFrameAndPivot(canvas, "垂直对称", 0.7f, 4, 0, 0);
245     }
246 
247     // 9. 对称 (水平对称)
248     void hDCMatrix(Canvas canvas){
249         matrix.reset();
250 
251         // 准备对称
252         float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
253         matrix.setValues(matrix_values);
254 
255         // 打印Matrix内数据
256         showMatrix();
257 
258         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
259         matrix.postTranslate(padding + (bitmapWidth + padding) * 1.6f, padding + (bitmapHeight + padding) * 4f);
260 
261         // 打印Matrix内数据
262         showMatrix();
263 
264         // 画出变换后的图像
265         canvas.drawBitmap(bitmap, matrix, paint);
266 
267         // 画出变形前的轮廓
268         drawFrameAndPivot(canvas, "水平对称", 1.6f, 4f, 0, 0);
269     }
270 
271     // 10. 对称(对称轴为直线y = x)
272     void dczMatrix(Canvas canvas){
273         matrix.reset();
274 
275         // 准备对称
276         float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
277         matrix.setValues(matrix_values);
278 
279         // 打印Matrix内数据
280         showMatrix();
281 
282         // 做下面的平移变换,纯粹是为了让变换后的图像和原图像不重叠
283         matrix.postTranslate(padding + (bitmapWidth + padding) * 3.6f, padding + (bitmapHeight + padding) * 4f);
284 
285         // 打印Matrix内数据
286         showMatrix();
287 
288         // 画出变换后的图像
289         canvas.drawBitmap(bitmap, matrix, paint);
290 
291         // 画出变形前的轮廓
292         drawFrameAndPivot(canvas, "对称轴为直线y = x", 3.6f, 4f, 0, 0);
293     }
294 
295 
296     //==============
297     @Override
298     protected void onDraw(Canvas canvas) {
299 
300         super.onDraw(canvas);
301 
302         // 画出原图像
303         canvas.drawBitmap(bitmap, matrix, paint);
304         drawFrameAndPivot(canvas, "原图", 0, 0, 0, 0);
305 
306         // 1. 平移
307         translateMatrix(canvas);
308 
309         // 2. 缩放
310         scaleMatrix(canvas);
311 
312         // 3. 旋转(默认中心点)
313         rotateMatrix2(canvas);
314 
315         // 4. 旋转(指定一个旋转角度和中心点)
316         rotateMatrix1(canvas);
317 
318         // 5. 错切 - 水平
319         hSkewMatrix(canvas);
320 
321         // 6. 错切 - 垂直
322         vSkewMatrix(canvas);
323 
324         // 7. 错切 - 水平 + 垂直
325         hvSkewMatrix(canvas);
326 
327         // 8. 对称 - 垂直
328         vDcMatrix(canvas);
329 
330         // 9. 对称 (水平对称)
331         hDCMatrix(canvas);
332 
333         // 10. 对称(对称轴为直线y = x)
334         dczMatrix(canvas);
335     }
336 
337 
338     public MatrixView(Context context) {
339         super(context);
340 
341         paint = new Paint(Paint.ANTI_ALIAS_FLAG);
342         PathEffect effects = new DashPathEffect(new float[]{5,5,5,5,5, 5}, 1);
343         paint.setStyle(Paint.Style.STROKE);
344         paint.setPathEffect(effects);
345 
346         matrix = new Matrix();
347         matrix.postTranslate(padding,padding);//默认位置
348         bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sophie);
349         bitmapWidth = bitmap.getWidth();
350         bitmapHeight = bitmap.getHeight();
351 
352     }
353 
354     @Override
355     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
356 //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
357         // Try for a bitmapWidth based on our minimum
358         int w  = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();//最小宽度
359 
360         if(w == 0) w = 480;
361         //计算最佳值,在其中解析了widthMeasureSpec,这时如果w大于widthMeasureSpec中的值,则选widthMeasureSpec
362         w = resolveSizeAndState(w, widthMeasureSpec, 0);
363 
364 
365         // Whatever the w ends up being, ask for a bitmapHeight that would let the pie
366         // get as big as it can
367         int h = getSuggestedMinimumHeight() + getPaddingBottom() + getPaddingTop();//最小高度
368 
369         if(h == 0) h = 800;//如果给出的建议是0,可以手动设置一个期望值。单位是像素。
370 
371         h = resolveSizeAndState(h, heightMeasureSpec, 0);//计算最佳值,在其中解析了heightMeasureSpec
372 
373 
374         //将量算的结果保存到View的成员变量mMeasuredWidth 和mMeasuredHeight中。
375         setMeasuredDimension(w, h);
376 
377         // 量算完成之后,View的父控件就可以通过调用
378         // getMeasuredWidth、getMeasuredState、getMeasuredWidthAndState
379         // 这三个方法获取View的量算结果。
380     }
381 
382 }

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

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

2.1 示例1

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

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

2.2 示例2

1     matrix.postTranslate(2f, 0f); 
2     matrix.preScale(0.2f, 1f); 
3     matrix.setScale(1f, 1f); 
4     matrix.postScale(5f, 1f); 
5     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 

 

posted @ 2016-08-02 16:38  f9q  阅读(582)  评论(0编辑  收藏  举报