Android-Matrix矩阵

来源:互联网 发布:windows视频截gif 编辑:程序博客网 时间:2024/06/05 22:56

一直很忙,也年底了,想了很多新一年的规划,也不知道能完成几个。。。年前发完版本,趁着空隙时间,看看Matrix相关的内容,顺便做做总结。

  • Matrix 原理
  • Matrix 的应用 - 压缩图像
  • Matrix 的应用 - 实现多点触控,控制图像缩放

Matrix

Matrix意思是矩阵,作为Android源码中的一个常用类,它的作用是持有一个3*3的矩阵数组,用于坐标的转换。Matrix作为Android开发中常用的数学工具,它的主要作用领域,主要作用于图像大小变换。

Matrix图片操作

我们可以看到Matrix矩阵如下

字面上理解,它们分别代表什么?
MSCALE 控制图像的缩放变换
MSKEW 控制图像的错切变换
MTRANS 控制图像的平移变换
MPSERSP 控制图像的透视

当然实际貌似并不是完全按照字面的意思去操作,在实际Android开发中,根据矩阵乘法的影响,我们知道实际上:
MSCALE_X, MSCALE_Y , MSKEW_X , MSKEW_Y 处理图像的旋转变换,
MTRANS_X, MTRANS_Y 处理图像的位移变换,
MSCALE_X, MSCALE_Y, MPERSP_2处理图像的缩放变换,
MSKEW_X, MSKEW_Y处理图像的错切变换。

Matrix的矩阵运算

Matrix有上述几种变换,包括位移、缩放、错切、透视,每一种变换都是对每一个像素点的操作,对于每一个像素点,我们可以用一个3行1列的数组矩阵来描述它

这里写图片描述

x代表像素点在屏幕上X轴的坐标,y代表像素点在屏幕上Y轴的坐标,1是像素点在屏幕上Z轴的坐标,Z轴数值为1代表默认值,如果Z轴值变大,相当于屏幕画布拉近,图像变大。

而当我们对一个像素点做变换操作,其实就是对像素点对应的矩阵做矩阵乘法运算,通过上面规定的三阶矩阵各个数值代表的变换,相乘之后来改变像素点的矩阵值,而改变像素点的位置。

这里复习一下矩阵的乘法
设A为m * p的矩阵,B为p n的矩阵,那么称m*n的矩阵C为矩阵A与B的乘积,记作 C = A B ,其中矩阵C中的第i行第j列元素可以表示为:
这里写图片描述

这是一个矩阵相乘的例子:
这里写图片描述

位移变换

位移变化其实也就是图像的每个像素点都实现了位移运算,以单独一个像素点来说,P(x0,y0)是起始点,P(x,y)是终点。

这里写图片描述

所做的矩阵运算

这里写图片描述

这里看到我们做的是后乘的矩阵运算,由一开始我们分析可知MSCALE_X, MSCALE_Y 控制图像的缩放变换,这两个值应该设置为△x和△y;MSCALE_X, MSCALE_Y控制图像旋转变换,由于此时大小不变,因为均为1;MPERSP_2控制图像的缩放,因此这个值也为1。

旋转变换

这里写图片描述

旋转变换,如果旋转的中心为原点,根据矩阵乘法的运算逆推,我们得出
MSCALE_X, MSCALE_Y , MSKEW_X , MSKEW_Y的值

这里写图片描述

缩放变换,错切变换

缩放变换,我们只需要控制的MSCALE_X, MSCALE_Y的值

如下图,k1,k2即为我们对图像的缩放比例,如果我们希望图像横纵比例都缩小一倍,那么k1,k2的值则为0.5,0.5。

这里写图片描述

错切变换,就是让每个像素点的横坐标(或者纵坐标)保持不变,而纵坐标(或者横坐标按比例变换)。

水平错切:
每一个像素点的坐标,纵坐标不变,横坐标则按比例发生了变化
这里写图片描述

垂直错切:
每一个像素点的坐标,横坐标不变,纵坐标则按比例发生了变化

这里写图片描述

如下图,k1的值控制水平错切,k2的值控制垂直错切

这里写图片描述

Matrix的前乘和后乘作用以及区别

对于上述的每种变换,Android提供了三种操作方式,分别是pre,set,post。例如位移变换,Matrix为我们提供了几个不同的API方法,分别是setTranslate,preTranslate,postTranslate,setTranslate代表直接设置三阶矩阵数值(直接覆盖原先已有的三阶矩阵),preTranslate代表让原来的三阶矩阵前乘另一个三阶矩阵,postTranslate代表让原来的三阶矩阵后乘另一个三阶矩阵。

为什么要这样区分呢?

这是为了让几种不同的变换操作同时起作用,比如我们有位移和缩放两个效果,如果我们只是调用setTranslate,那么之前设置的变换效果会被覆盖。为了让多种变换效果复合,我们需要采用前乘或者后乘的设置方式。

我们来看例子,

测试案例1

这是什么操作都不做的情况,直接显示位图:

这里写图片描述

测试案例2

我们调用set方法,给位图加上位移和缩放的效果:

        Matrix matrix = new Matrix();        matrix.setTranslate(150, 150);        matrix.setScale(0.5f, 0.5f);        Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap                .ic_launcher);        canvas.drawBitmap(bitmap, matrix, paint);

效果图:

这里写图片描述

我们看到我们设置了两个效果,但是位移效果最后会被缩放效果覆盖。

测试案例3

现在我们使用pre方法,也还是给位图加上位移和缩放的效果:

        Matrix matrix = new Matrix();        matrix.setScale(0.5f, 0.5f);        matrix.preTranslate(150, 150);        Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap                .ic_launcher);        canvas.drawBitmap(bitmap, matrix, paint);

效果图:

这里写图片描述

测试案例4

然后我们再尝试post方法,也还是给位图加上位移和缩放效果:

        Matrix matrix = new Matrix();        matrix.setScale(0.5f, 0.5f);        matrix.preTranslate(150, 150);        Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap                .ic_launcher);        canvas.drawBitmap(bitmap, matrix, paint);

效果图

这里写图片描述

从上面我们可以看到,通过调用preXXX或者postXXX能够实现图像变换效果的复合,但是我们可以发现,调用preXXX和调用postXXX的结果是不一样的,原因就是矩阵乘法中前乘和后乘得到的结果是不一样的。
以上的例子来计算:
我们调用

matrix.setScale(0.5f, 0.5f);matrix.preTranslate(150, 150);

Matrix会通过前乘来计算出最后的结果:

这里写图片描述

而我们调用
matrix.setScale(0.5f, 0.5f);
matrix.preTranslate(150, 150);

Matrix会调用后乘来计算得出最后的结果:

这里写图片描述

通过上面分析我们知道,前乘和后乘能够带来两种不同效果。此时,通过后乘达到的效果与我们预期的不相符。

1)如果我们希望变换效果按照代码的执行顺序,那么我们可以调用postXXX;比如我们希望先缩小0.5倍,再位移150个单位,这时候调用preXXX。

2)如果我们希望后添加的变换效果也会被之前变换效果影响,那么我们调用postXXX;比如我们先设置了缩小效果,但是我们后续添加位移的效果,那么此时位移效果会被缩小效果影响,就像我们设置了0.5f的缩放值,后面通过postTranlate位移100单位,最后却只会位移50个单位。

Matrix应用 - 压缩图像

首先,我们可以用它来对图像进行压缩处理

    private Bitmap scaleBitmap(Bitmap bitmap, int targetWidth, int targetHeight) {        Matrix matrix = new Matrix();        matrix.setScale(targetWidth / bitmap.getWidth(), targetHeight / bitmap.getHeight());        return Bitmap.createBitmap(bitmap, 0, 0, targetWidth, targetHeight, matrix, true);    }

Matrix应用 - 实现图片的多点触控

先来说我们的目标,是实现一个能够使图片感应手指多点触控放大缩小的功能,通过上面Matrix的铺垫,我们知道Matrix可以操作图像的缩放,这里还有一个点,就是多点触控。

多点触控

思路一,直接处理onTouchEvent()

MotionEvent.ACTION_DOWN:在第一个点被按下时触发
MotionEvent.ACTION_UP:当屏幕上唯一的点被放开时触发
MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有一个点被按住,此时再按下其他点时触发。
MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)。
MotionEvent.ACTION_MOVE:当有点在屏幕上移动时触发。

通过event.getX(index)和event.getY(index)可以获取到指定index点的坐标

   /**     * Returns the X coordinate of this event for the given pointer     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer     * identifier for this index).     * Whole numbers are pixels; the      * value may have a fraction for input devices that are sub-pixel precise.      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0     * (the first pointer that is down) to {@link #getPointerCount()}-1.     *     * @see #AXIS_X     */    public final float getX(int pointerIndex) {        return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);    }

相对的,evetn.getX()就是获取第一个按下的手指的坐标

/**     * {@link #getX(int)} for the first pointer index (may be an     * arbitrary pointer identifier).     *     * @see #AXIS_X     */    public final float getX() {        return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);    }

思路二,通过GestureDetector辅助完成

我们知道Android为我们提供了GestureDetector,通过将触摸事件托管给它,能够为我们实现滑动,双击等操作的响应方法,为了方便多点触摸放大缩小的监听,我们也可以用辅助类ScaleGestureDetector。

交付触摸事件给ScaleGestureDetector处理

   @Override    public boolean onTouchEvent(MotionEvent event) {        return mScaleGestureDetector.onTouchEvent(event);    }

简单地实现从屏幕中心放大缩小的效果

    private class MatrixScaleGestureDetector implements android.view.ScaleGestureDetector            .OnScaleGestureListener {        @Override        public boolean onScale(ScaleGestureDetector detector) {            Matrix matrix = getImageMatrix();            matrix.postScale(detector.getScaleFactor(), detector.getScaleFactor(),                     getWidth() / 2, getHeight() / 2);            setImageMatrix(matrix);            invalidate();            return true;        }        @Override        public boolean onScaleBegin(ScaleGestureDetector detector) {            return true;        }        @Override        public void onScaleEnd(ScaleGestureDetector detector) {        }    }

当然还有一些更加酷的效果,类似魅族手机的相册功能,相册图像能够根据手势进行旋转。

demo地址:https://github.com/82367825/MatrixMaster

更加高级的功能实现,我们能看到像这样的折叠菜单效果:
http://blog.csdn.net/lmj623565791/article/details/44283093

类似Matrix这样的数学矩阵在Android中不止用于操作图像大小和变换,更有ColorMatrix用于操作图像色彩,实现各种滤镜效果:
http://www.it165.net/pro/html/201505/40131.html

参考内容:
http://www.cnblogs.com/qiengo/archive/2012/06/30/2570874.html
http://blog.csdn.net/cquwentao/article/details/51445269

0 0
原创粉丝点击