matrix知识点小记

最近从github上面下载了一个手势与view交互的组件StickerView,里面有一块关于矩阵的代码没看明白,研究了下矩阵。

可以参考 http://my.oschina.net/lifj/blog/175114

http://blog.sina.com.cn/s/blog_783ede030102w6we.html

Matrix 在View进行缩放,旋转,移动时会用到。

Matrix内置了一个3*3的矩阵

cosx ,-sinx, translatex

sinx , cosx, translatey

0     ,      0,  scale

这四种操作的内部实现过程都是通过matrix.setValues(…)来设置矩阵的值来达到变换图片的效果。
     Matrix的每种操作都有set、pre、post三种操作,set是清空队列再添加,pre是在队列最前面插入,post是在队列最后面插入。
     pre方法表示矩阵前乘,例如:变换矩阵为A,原始矩阵为B,pre方法的含义即是A*B
     post方法表示矩阵后乘,例如:变换矩阵为A,原始矩阵为B,post方法的含义即是B*A

1.matrix.preScale(0.5f, 1);   
2.matrix.preTranslate(10, 0);  
3.matrix.postScale(0.7f, 1);    
4.matrix.postTranslate(15, 0);  
等价于:
translate(10, 0) -> scale(0.5f, 1) -> scale(0.7f, 1) -> translate(15, 0)
注意:后调用的pre操作先执行,而后调用的post操作则后执行。

set方法一旦调用即会清空之前matrix中的所有变换,例如:
1.matrix.preScale(0.5f, 1);   
2.matrix.setScale(1, 0.6f);   
3.matrix.postScale(0.7f, 1);   
4.matrix.preTranslate(15, 0);  
等价于
translate(15, 0) -> scale(1, 0.6f) ->  scale(0.7f, 1)

matrix.preScale (0.5f, 1)将不起作用。

matrix API:

public Matrix()

创建一个标准矩阵,应用后点的坐标不会有任何改变

public Matrix(Matrix src)

创建一个src的深度复制,改变规则与src一致

 

public boolean equals(Object obj)

如果obj为Matrix并且它的值与当前Matrix对象相等的会将会返回true

public void getValues(float[] values)

获取matrix的那9个值

public boolean invert(Matrix inverse)

将当前矩阵反转,并且反转后的值存入inverse中,如果当前矩阵不能反转,那么 inverse不变,返回false,反转规则应该是满足 当前矩阵*inverse=标准矩阵,标准矩阵为[1,0,0,0,1,0,0,0,1];不过其实也不用想得那么复杂,比如当前matrix是 setTranslate(10,20),那么反转后的matrix就是setTranslate(-10,-20);

public boolean isIdentity()

判断当前矩阵是否为标准矩阵,这个函数比if (getType() == 0)运行的可能更快一些

public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)

用当前矩阵改变src中的点的坐标,然后将改变后的值对应的存入dst数组中,其中pointCount表示点的数目,(x,y)=(src[2*k],src[2*k+1])表示一个点的坐标,k取整数值,安卓中用数组存储点的坐标值的时候都是按如此法则存储的。

public void mapPoints(float[] pts)

用当前矩阵改变pts中的值,然后存储在pts中,同上,pts也是存储点的坐标的数组

public void mapPoints(float[] dst, float[] src)

用当前矩阵改变src的值,并且存储到数组dst中

public float mapRadius(float radius)

将一个半径为radius的圆的所有点坐标用matrix进行变换后,计算出该圆的半径并且返回该值。注意:要得到正确的值该圆默认是有中心的,个人注:说实话不明白这个函数有什么用

public boolean mapRect(RectF dst,RectF src)

用matrix改变src的4个顶点的坐标,并将改变后的坐标调整后存储到dst中,(RectF只能存储改变后的左上角和右下角坐标,所以需要调整),返回的值跟rectStaysRect()一样,从字面意思可以认为src改变后仍然是RectF,那么就返回true

public boolean mapRect(RectF rect)

用matrix改变rect的4个顶点的坐标,并将改变后的坐标调整后存储到rect当中

public void mapVectors(float[] dst, float[] src)

用matrix改变dst中的向量值并且存储到src当中,注意:setTranslate(x,y)这样的matrix调用了这个函数后不会有任何反应,这样的matrix应该调用mapPoints

public void mapVectors(float[] vecs)

用matrix改变vecs中的值并且存储到vecs当中,同上,注意:setTranslate(x,y)这样的matrix调用了这个函数后不会有任何反应,这样的matrix应该调用mapPoints

public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)

同上,只不过vectorCount表示向量的数目,dstIndex,srcIndex分别表示各自的起始位置

public boolean postConcat(Matrix other)

将当前的matrix连接到other之后,并且将连接后的值写入当前matrix。 M‘=other*M,连接后变换的效果,相当于先变换M,然后在other变换

public boolean postRotate(float degrees)

相当于这样:Matrix other=newMatrix();other.setRotate(degrees);postConcat(other);

先创建设置一个以0,0为原点旋转degrees度的矩阵other,然后将当前的matrix连接到other之后,并且将连接后的值写入当前matrix。

public boolean postRotate(float degrees, float px, float py)

同上,不过改成这样Matrix other=newMatrix();other.setRotate(degrees,px,py);postConcat(other);

public boolean postScale(float sx, float sy)

同上,无非是改成other.setScale(sx,sy);

public boolean postScale(float sx, float sy, float px, float py)

同上

public boolean postSkew(float kx, float ky)

public boolean postSkew(float kx, float ky, float px, float py)

public boolean postTranslate(float dx, float dy)

都是一样的,不过是创建的other有所不一样而已

public boolean preConcat(Matrix other)

public boolean preRotate(float degrees)

public boolean preRotate(float degrees, float px, float py)

public boolean preScale(float sx, float sy)

public boolean preScale(float sx, float sy, float px, float py)

public boolean preSkew(float kx, float ky)

public boolean preSkew(float kx, float ky, float px, float py)

public boolean preTranslate(float dx, float dy)

同上边对应的函数功能类似,无非是other被连接在当前matrix之后,然后将连接后的值写入当前matrix当中

public boolean rectStaysRect()

如果该matrix可以将rectF变换成rectF,那么该函数返回true,在标准变换,伸缩变换,平移变换,和多个90度的旋转变换时,该函数是返回true的

public void reset()

将当前matrix设置为标准矩阵

public void set(Matrix src)

将src的内容深度复制给当前矩阵,如果src为null,那么当前矩阵变为标准矩阵

public boolean setConcat(Matrix a,Matrix b)

将当前matrix的值变为a和b的乘积

public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)

将当前matrix的值设置为这样的值,对src变换后可以得到dst的数据,pointCount表示点的数目,只能是0-4。设置成功返回true

public boolean setRectToRect(RectF src,RectF dst, Matrix.ScaleToFit stf)

将当前matrix的值设置为这样的值,对src进行变换后可以得到dst,因两者都是RectF,所以该matrix的值只能是伸缩和平移的组合,设置成功了返回true,stf为伸缩参数,这个Matrix.ScaleToFit伸 缩参数有什么名堂呢,它有四个常量,每个常量应用后会导致matrix有什么结果呢,根据那4个常量的文字说明可知,CENTER,END,START表 示得到的伸缩矩阵m,m对src进行变换后得到dst1,dst1跟src有同样的宽高比例,dst1在dst的内部,不同的地方是CENTER的状态是 这样的:dst1.left-dst.left=dst.right-dst1.right,dst1.top-dst.top=dst.bottom- dst1.bottom;END的状态是这样的:dst1.right=dst.right,dst1.bottom=dst.bottom.START 的状态是这样的:dst1.left=dst.left,dst1.top=dst.top;至于FILL表示得到的伸缩矩阵m,通过它对src变换后得 到的Rect就是dst,完全重合。结论通过RectF(0,0,10,10),  RectF(0,0,20,30)这两个矩阵得到了验证。

public void setRotate(float degrees)

设置当前matrix,使作用于点坐标时使点坐标以点(0,0)为原点旋转degrees度。

public void setRotate(float degrees, float px, float py)

设置当前matrix,使作用于点坐标时使点坐标以点(px,py)为原点旋转degrees度。在转换过程中,该原点不可改变

public void setScale(float sx, float sy, float px, float py)

设置当前matrix,使作用于点坐标时使点坐标以(px,py)为支点伸缩sx,sy倍。(px,py)在转换过程中不能改变。这个解释有点蒙,验证了下发现其实就是x'=(x+px)*sx,y'=(y+py)*sy

public void setScale(float sx, float sy)

这其实就是setScale(sx,sy,0,0);

public void setSinCos(float sinValue, float cosValue)

这其实就是setSinCos(sinValue,cosValue,0,0);

public void setSinCos(float sinValue, float cosValue, float px, float py)

设置当前matrix,以px,py为支点进行旋转变换,变换方式与 sinValue,cosValue的值有关,经过验证,可以得到近似换算公式为:x'=cosValue*x-sinValue*y+(1- cosValue)*px+sinValue*py;y'=sinValue*x+cosValue*y-sinValue*px+(1-cosValue)*py;

public void setSkew(float kx, float ky, float px, float py)

设置当前matrix,以px,py为支点进行倾斜kx,ky.公式变换应该为x'=x+kx*(y-py),y'=ky*(x-px)+y;

public void setSkew(float kx, float ky)

相当于setSkew(kx,ky,0,0);

public void setTranslate(float dx, float dy)

设置matrix,应用时使点坐标(x,y)各自平移为(x+dx,y+dy);

public void setValues(float[] values)

复制9个数据给matrix,由于matrix的变形,或许这些数据会变成16位的数据,所以用getValues()可能不能得到与初始化相同的数据。不出意外的话,values的后三位要是0,0,1,否则可能该matrix变化后得不到你想要的点坐标

public String toShortString ()

public String toString ()

返回一个字符串用来描述该目标和数据


下面这段代码是

StickerView里面的一段关于矩阵的代码


            mOriginPoints = new float[]{0, 0, px, 0, px, py, 0, py, px / 2, py / 2};//矩形框5个点的坐标,四个角与中心点
            mOriginContentRect = new RectF(0, 0, px, py);//图片外面的矩形框
            mPoints = new float[10];
            mContentRect = new RectF();
            mMatrix = new Matrix();

            float transtLeft = ((float)DisplayUtil.getDisplayWidthPixels(getContext()) - mBitmap.getWidth()) / 2;
            float transtTop = ((float)DisplayUtil.getDisplayWidthPixels(getContext()) - mBitmap.getHeight()) / 2;
            mMatrix.postTranslate(transtLeft, transtTop);//将图片平移到这个坐标


接着是draw方法

   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBitmap == null || mMatrix == null) {
            return;
        }
        mMatrix.mapPoints(mPoints, mOriginPoints);//矩阵已经同过平移变化,该方法是用当前矩阵改变mOriginPoints数组里面的值得到平移后的5个点坐标,存入mPoints
        Log.d("aa", mMatrix.toString()+"=================");      Log.d("aa",mPoints[0]+"="+mPoints[1]+"="+mPoints[2]+"="+mPoints[3]+"="+mPoints[4]+"="+mPoints[5]+"="+mPoints[6]+"="+mPoints[7]+"="+mPoints[8]+"="+mPoints[9]+"=");
        mMatrix.mapRect(mContentRect, mOriginContentRect);//同上
        Log.d("aa", mMatrix.toString()+">>>>>>>>>>>>>>>");
        canvas.drawBitmap(mBitmap, mMatrix, mPaint);
        if (mDrawController && isFocusable()) {
            canvas.drawLine(mPoints[0], mPoints[1], mPoints[2], mPoints[3], mBorderPaint);//根据四个角坐标画四根线
            canvas.drawLine(mPoints[2], mPoints[3], mPoints[4], mPoints[5], mBorderPaint);
            canvas.drawLine(mPoints[4], mPoints[5], mPoints[6], mPoints[7], mBorderPaint);
            canvas.drawLine(mPoints[6], mPoints[7], mPoints[0], mPoints[1], mBorderPaint);
            canvas.drawBitmap(mControllerBitmap, mPoints[4] - mControllerWidth / 2, mPoints[5] - mControllerHeight / 2, mBorderPaint);//4,5表示的坐标是控制图标的位置,也就是矩形右下角的坐标
            canvas.drawBitmap(mDeleteBitmap, mPoints[0] - mDeleteWidth / 2, mPoints[1] - mDeleteHeight / 2, mBorderPaint);//0,1表示的坐标是删除图标的位置,也是矩形左上角坐标
        }
    }


可以看到我红色字体标出的部分,首先他将图片四个角坐标与中心点坐标放在数组中,然后是 mMatrix.postTranslate(transtLeft, transtTop);矩阵平移

接着有一个方法是mapPoints方法,困扰我的地方就在这里,查了很久都找不到这个方法到底是什么意思

按照android官网的说法是 Apply this matrix to the array of 2D points specified by src, and write the transformed points into the array of

 points specified by dst.

应用这个矩阵二维点指定的src数组,写到新的数组中。

意思不明确,我将matrix.toString()打印出来,然后将mPoints数组打印出来

结果

Matrix{[1.0, 0.0, 204.0][0.0, 1.0, 204.0][0.0, 0.0, 1.0]}
 204.0,204.0,276.0,204.0,276.0,276.0,204.0,276.0,240.0,240.0

原始数组 0, 0, px, 0, px, py, 0, py, px / 2, py / 2

结果很明显了:matrix打印出来的值是矩阵通过平移变换得到的3*3矩阵

mPoints数组里面存放的是平移之后的5个点的坐标。


查了api,可以发现matrix提供了很多map映射坐标的方法,这样的方法可以很快的获得view平移,旋转,缩放后的坐标

猜你喜欢

转载自blog.csdn.net/hyjwan/article/details/46813823