之前我们说过对于图片的颜色的变换,我们可以用颜色矩阵进行变化,相应的Android对于图像的位置也可以位置矩阵对位置进行变换,我们来说下Android中的位置矩阵是什么样子的格式。
我们可以发现其实他每个像素的矩阵就是3个元素值,分别是X轴坐标和Y轴坐标,以及一个1(这个1是一直都不会改变的)。然后通过矩阵乘法得到一个新的位置矩阵,然后图像就进行变换了,位置的初始矩阵和颜色的初始矩阵一样都是对角线全为1,其他全为0。主要有下面几个变换:平移,旋转,缩放,错切,然后这些变换我们来讲讲它是怎么做到的。
1、平移
平移其实不用说都懂,就是X' = X + dx, Y' = Y + dy
,然后我们就可以发现我们可以在初始矩阵的基础上改变成下面这个矩阵就可以达到这个效果:
2、旋转
就是把每个点以原点为中心旋转一个角度,所以因为旋转离原点的距离不变,所以我们可以发现旋转就是满足下面这个变换式子的,然后我们也就可以构造出这个矩阵:
3、缩放
其实把图像进行缩放就是把图像的每个像素的位置进行改变,所以就是X' = X * k1, Y' = Y * k2
然后我们就可以构造下面这个矩阵了:
4、错切
这个原来我们就没有接触过了,这个是什么呢?错切就是在某方向上,按照一定的比例对图形的每个点到某条平行于该方向的直线的有向距离做放缩得到的平面图形,可以看下图。公式就是X' = X + Y * k1, Y' = Y + X * k2
然后我们就可以构造出下面这个矩阵:
其实我们就可以发现一个规律:a、e控制的是图像的缩放,c、f控制的是图像的平移,b、d控制的是图像的错切。
知道了这些以后我们其实就很容易写出位置变换的功能了。其实和颜色变换是一样的,我们构造出9个按矩阵格式排列的EditText,然后我们取出他们的值赋给一个矩阵,然后对图片进行变换就好了,但是我们想看出对比,所以我们就写了一个自定义View,在同一个位置放两张图片,这样的话对比就很明显了,接下来就来说下怎么写吧。
我们只要暴露出一个方法把矩阵进行赋值,然后我们就在一个画布上现把不变的图片画出来,之后我们就把图片和矩阵一起传进去再画一个图像就好了。
package com.xjh.gin.image;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Gin on 2018/2/9.
*/
public class ImageMatrixView extends View {
private Bitmap mBitmap;
private Matrix mMatrix;
public ImageMatrixView(Context context) {
super(context);
initView();
}
public ImageMatrixView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public ImageMatrixView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
setImageMatix(new Matrix());
}
public void setImageMatix(Matrix matrix){
mMatrix=matrix;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap,0,0,null);
canvas.drawBitmap(mBitmap,mMatrix,null);
}
}
然后我们像颜色矩阵的时候一样,通过GridLayout生成一个3*3的格子,然后我们在代码中把EditText设置进去就好了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.xjh.gin.image.ImageMatrixView
android:id="@+id/view"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"/>
<GridLayout
android:id="@+id/group2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:columnCount="3"
android:rowCount="3">
</GridLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnChange1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Change" />
<Button
android:id="@+id/btnReset1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Reset" />
</LinearLayout>
</LinearLayout>
private void initView() {
btn_Change = (Button) findViewById(R.id.btnChange1);
btn_Reset = (Button) findViewById(R.id.btnReset1);
mImageMatrixView = (ImageMatrixView) findViewById(R.id.view);
mGridlayout = (GridLayout) findViewById(R.id.group2);
}
private void initEvent(){
mGridlayout.post(new Runnable() {//在onCreate方法中因为控件还没有绘制完毕,所以我们无法获得控件的高和宽,所以当控件绘制完毕以后,我们就在线程中获取,因为这时候控件一定绘制完毕了
@Override
public void run() {
mEdWidth = mGridlayout.getWidth() / 3;
mEdHeight = mGridlayout.getHeight() / 3;
addEts();
initImageMatrix();
}
});
btn_Change.setOnClickListener(this);
btn_Reset.setOnClickListener(this);
}
private void addEts() {//添加EditText
for (int i = 0; i < 9; i++) {
EditText editText = new EditText(ImageMatrix.this);
editText.setGravity(Gravity.CENTER);
mEts[i] = editText;
mGridlayout.addView(editText, mEdWidth, mEdHeight);//让editText动态的加载进mGroup中去
}
}
然后我们就是先把矩阵的值给初始化一下,以及我们要有一个方法获取EditText的值,然后传入自定义控件中去对图像进行处理。
private void initImageMatrix() {
for (int i = 0; i < 9; i++) {
if (i % 4 == 0) {
mEts[i].setText(String.valueOf(1));
} else {
mEts[i].setText(String.valueOf(0));
}
}
}
private void getImageMatrix(){
for(int i=0;i<9;i++){
EditText editText = mEts[i];
mImageMatrix[i]=Float.parseFloat(editText.getText().toString());
}
}
然后我们对于两个按钮进行点击事件的监听就好了,然后这个功能就实现了。
public void onClick(View v) {
switch (v.getId()){
case R.id.btnChange1:
getImageMatrix();
Matrix matrix1 = new Matrix();
matrix1.setValues(mImageMatrix);
mImageMatrixView.setImageMatix(matrix1);
mImageMatrixView.invalidate();//刷新图片
break;
case R.id.btnReset1:
initImageMatrix();
getImageMatrix();
Matrix matrix2 = new Matrix();
matrix2.setValues(mImageMatrix);
mImageMatrixView.setImageMatix(matrix2);
mImageMatrixView.invalidate();//刷新图片
break;
}
}
最后其实上面都是些废话,因为Android的API帮我们已经很好的实现了这个功能的封装
matrix1.setRotate(30);//旋转(角度)
matrix1.setTranslate(150,150);//平移(X轴偏移量,Y轴偏移量)
matrix1.setScale(2,2);//缩放(X轴的缩放量,Y轴的缩放量)
matrix1.setSkew(2,3);//错切(X轴的缩放量,Y轴的缩放量)
而且我们可以对于矩阵进行组合,我们只要把第一个用上面的,然后后面的都用下面的就好了。
matrix1.postRotate(30);//旋转
matrix1.postTranslate(150,150);//平移(X轴偏移量,Y轴偏移量)
matrix1.postScale(2,2);//缩放(X轴的缩放量,Y轴的缩放量)
matrix1.postSkew(2,3);//错切
这样我们就实现了对图像的位置变换。