1.Matrix乘法基本知识
两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。如A是m×n矩阵和B是n×p矩阵,它们的乘积C是一个m×p矩阵 。
简单的理解就是第一个矩阵A的行和第二个矩阵B的列对应的元素相乘之后的和,前提是A的列和B的行相等,才能相乘。
例如:
这里我们对矩阵乘法有个基本了解了,就是行乘列。第一个矩阵的行和第二个矩阵的列中元素一一对应相乘在求和。
2.Android中Matrix的基本知识
Android中Matrix是一个3行3列的矩阵,如图:
Matrix的对图像的处理可分为四类基本变换:
Translate 平移变换
Rotate 旋转变换
Scale 缩放变换
Skew 错切变换
对于Matrix对图像处理的变换可以参考下面这篇文章Android Matrix
这里就简单的说下,如果想平移图片,就只要设置矩阵中MTRANS_X,和MTRANS_Y的值即可,其他参数类似,修改其对应的值即可。
3.Android中Matrix类
Android中Matrix的基本知识也有个大概的认识,我可以来看下Android为我们提供的Matrix类,该有以前几个方法:
(假设当前矩阵为A,XXX为B的矩阵)
setXXX:设置其为指定的矩阵 (结果:B)
preXXX:先乘即矩阵的右乘(结果:A*B)
postXXX:后乘即矩阵的左乘(结果:B*A)
左乘右乘可能比较好理解就是在矩阵的左边乘还是右边乘,先乘和后乘可能就有点费劲,我之前也在这个问题上花了不少时间,后面终于搞明白了。因为图像中,越靠近右边的矩阵越先执行,所以,右乘可能是先被执行的即先乘。这样,右乘和先乘对应,左乘和后乘对应啦。
可以参考这篇文章Android Matrix理论与应用详解中有下面这段话
给定的图像依次进行了基本变化F1、F2、F3…..、Fn,它们的变化矩阵分别为T1、T2、T3…..、Tn,图像复合变化的矩阵T可以表示为:T = TnTn-1…T1。
当然,在实际中,如果首先指定了一个matrix,比如我们先setRotate(),即指定了上面变换矩阵中,中间的那个矩阵,那么后续的矩阵到底是pre还是post运算,都是相对这个中间矩阵而言的。
4.Android中Matrix类的举例
先看下效果图
再来看下工程结构
这是我们只要实现MatrixView这个类,以及导入a.png这张图,然后在activity_main.xml中做相应的布局就好了。接下来看下具体的代码实现和布局
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Spinner
android:id="@+id/id_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.example.matrixtestdemo.MatrixView
android:id="@+id/id_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffff00" />
</LinearLayout>
MatrixView.java的类结构
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class MatrixView extends View{
private Bitmap mBitmap;
private Matrix mMatrix;
// private Paint mPaint;
private int mSrcWidth;
private int mSrcHeight;
private int mSrcCenterY;
private int mSrcCenterX;
private boolean mIsMirror;
public MatrixView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MatrixView(Context context) {
super(context);
init();
}
private void init() {
mMatrix = new Matrix();
// mPaint = new Paint();
}
public void setSrcBitmap(Bitmap bmp) {
mBitmap = bmp;
mSrcWidth = mBitmap.getWidth();
mSrcHeight = mBitmap.getHeight();
mSrcCenterX = mSrcWidth/2;
mSrcCenterY = mSrcWidth/2;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mIsMirror) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("TAG", "onDraw:"+mMatrix.toString());
}
/**
* 平移
* @param dx像素
* @param dy像素
*/
public void thanslate(float dx, float dy) {
mMatrix.reset();
mMatrix.setTranslate(dx, dy);
postInvalidate();
}
/**
* 旋转
* @param degrees角度 正为顺时针,负为逆时针
*/
public void rotate(float degrees) {
mMatrix.reset();
mMatrix.setRotate(degrees);
postInvalidate();
}
/**
* 缩放
* @param sx x轴像素缩放
* @param sy y轴像素缩放
*/
public void scale(float sx, float sy) {
mMatrix.reset();
mMatrix.setScale(sx, sy);
postInvalidate();
}
/**
* 错切
* @param kx
* @param ky
*/
public void skew(float kx, float ky) {
mMatrix.reset();
mMatrix.setSkew(kx, ky);
postInvalidate();
}
/**
* 由中心点进行缩放
* @param sx x轴像素缩放
* @param xy y轴像素缩放
*/
public void scaleByCenter(float sx, float sy) {
mMatrix.reset();
mMatrix.postTranslate(-mSrcCenterX, -mSrcCenterY);
mMatrix.postScale(sx, sy);
mMatrix.postTranslate(mSrcCenterX, mSrcCenterY);
postInvalidate();
}
/**
* 由中心点进行旋转
* @param sx x轴像素缩放
* @param xy y轴像素缩放
*/
public void rotateByCenter(float degrees) {
mMatrix.reset();
mMatrix.postTranslate(-mSrcCenterX, -mSrcCenterY);
mMatrix.postRotate(degrees);
mMatrix.postTranslate(mSrcCenterX, mSrcCenterY);
postInvalidate();
}
/**
* 水平镜像
*/
public void horizontalMirror() {
mMatrix.reset();
mMatrix.setScale(-1, 1);
mMatrix.postTranslate(2*mSrcWidth, 0);
postInvalidate();
}
/**
* 垂直镜像
*/
public void verticalMirror() {
mMatrix.reset();
mMatrix.setScale(1, -1);
mMatrix.postTranslate(0, 2*mSrcHeight);
postInvalidate();
}
/**
* 设置是否显示原bitmap的位图
* @param isMirror
*/
public void setMirror(boolean isMirror) {
mIsMirror = isMirror;
}
}
MainActivity.java 实现MatrixView的方法调用实现
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
public class MainActivity extends Activity {
private String[] mModel = new String[] {
"平移",
"放大",
"缩小",
"旋转",
"由中心点放大",
"由中心点缩小",
"由中心点旋转",
"错切",
"水平镜像",
"垂直镜像",
};
private MatrixView mMatrixView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
Spinner spinner = (Spinner) findViewById(R.id.id_spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mModel);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
mMatrixView.setMirror(false);
switch (position) {
case 0:
mMatrixView.thanslate(100, 100);
break;
case 1:
mMatrixView.scale(2, 2);
break;
case 2:
mMatrixView.scale(0.5f, 0.5f);
break;
case 3:
mMatrixView.rotate(45);
break;
case 4:
mMatrixView.scaleByCenter(2, 2);
break;
case 5:
mMatrixView.scaleByCenter(0.5f, 0.5f);
break;
case 6:
mMatrixView.rotateByCenter(45f);
break;
case 7:
mMatrixView.skew(0.5f, 0.5f);
break;
case 8:
mMatrixView.setMirror(true);
mMatrixView.horizontalMirror();
break;
case 9:
mMatrixView.setMirror(true);
mMatrixView.verticalMirror();
break;
default:
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mMatrixView = (MatrixView) findViewById(R.id.id_imageview);
mMatrixView.setSrcBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.a));
}
}
5.总结
android中对于Matrix这个类的set,pre,post方法确实不是很理解的话,记住post的用法就好了。当你一个图片想做平移(100,0),旋转(40),发大(2,2),那么直接使用post,调用循序是和这个一样的,如
postTranslate(100,0);
postRotate(40);
postScale(2,2);
这样就可以实现上面那个想要的效果了。还有记住一点就是越靠近右边的矩阵越先执行,这样理解起来相对来说就比较简单了。
最后要说的是,这个坐标轴的原点是不会根据这些函数的调用而改变的,所以坐标轴的原点一直是左上角,这样就是为什么我们要平移图片来达到图片绕中心点旋转或者放大缩小了。因为缩放,旋转都是以坐标原点为中心点来处理的。
实践出真知,程序中更是如此一定要多动手,多动脑,这样对于个人代码水平提升是有很大帮助的,那些复杂的图形变化,也是基于最基本的变化结构来的,弄懂了基本的,其他的就是只是组合了。