Android Matrix原理及运用

1 Matrix 在Android 中的运用

  • ImageView 的ScaleType,通过 setImageMatrix(mMatrix) 方法,可以自定义一个Matrix。 对 Bitmap 做相应的处理,如缩放,位移。
  • Matrix 可以对 Rect 和 点的集合做变换处理
    public void mapPoints(float[] pts) {
        mapPoints(pts, 0, pts, 0, pts.length >> 1);
    }
    public boolean mapRect(RectF rect) {
        return mapRect(rect, rect);
    }
  • Android 中 Path 路径也可以做相应的 Matrix 变换
    /**
     * Transform the points in this path by matrix.
     *
     * @param matrix The matrix to apply to the path
     */
    public void transform(Matrix matrix) {
        isSimplePath = false;
        nTransform(mNativePath, matrix.native_instance);
    }

2 矩阵的乘法运算

以下内容摘自百度百科
矩阵乘法的前提条件 : A*B = C ,A 的列数 和 B的行数相等 。 C 的行数等于 A 的行数 ,列数等于B的列数
矩阵的乘法 满足结合律 。不满足交换律
乘法结合律: (AB)C=A(BC)
乘法交换律: (AB)!= (BA) 。

在这里插入图片描述
矩阵乘法的实际案例 :
在这里插入图片描述

3 单位矩阵

Android 中矩阵的实体结构

在这里插入图片描述
从字面上就可以理解 :

  • MTRANS_X 是和 位移相关
  • MSCALE_X 是和 缩放相关
  • MSKEW_X 是和 错切相关
    其中 最后一行 的 MPERSP 与 3d 变换有关 。本文不做解析。

在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1。除此以外全都为0。

当我们New 一个Matrix 的时候 ,创建的是 单位矩阵
单位矩阵的特点:任何矩阵与单位矩阵相乘都等于本身,无论左乘右乘
在这里插入图片描述
测试New 的Matrix 矩阵 实体:

        Matrix matrix = new Matrix();
        Log.e("tag", "MatrixTest" + matrix.toShortString());

Log 打印如下 : MatrixTest[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]

单位矩阵的这种性质,在我们 对Matrix 只做单次变换时 ,例如 preTranslate 和 postTranslate 是等价的。
在这里插入图片描述

4 Android 中Matrix Pre ,Post变换对应的矩阵乘法

不管是图形,文字,图片,都是由无数个坐标点形成的。对坐标点的变化才是最根本的变换。
Android 中 点的坐标 的表示
在这里插入图片描述
上面的3*3 的 变化矩阵 右乘 坐标矩阵。就是 变化后的坐标

pre 操作代表 右乘 M(原始) * M( 位移变化)

    /**
     * Preconcats the matrix with the specified translation.
     * M' = M * T(dx, dy)
     */
    public boolean preTranslate(float dx, float dy) {
        return native_preTranslate(native_instance, dx, dy);
    }

post 操作 代表 左乘 M( 位移变化) * M(原始)

    /**
     * Postconcats the matrix with the specified translation.
     * M' = T(dx, dy) * M
     */
    public boolean postTranslate(float dx, float dy) {
        return native_postTranslate(native_instance, dx, dy);
    }

5 Pre ,Post 的执行顺序 。

我们使用 Matrix 对坐标点point 的变化如下

        Matrix matrix = new Matrix();
        matrix.preTranslate(1000, 1000);
        matrix.preScale(0.5f, 0.5f);
        matrix.postScale(0.5f, 0.5f);
        matrix.postTranslate(1000, 1000);
        float[] point = new float[2];
        point[0] = 0;
        point[1] = 0;
        matrix.mapPoints(point);

最终会转换成 矩阵的乘法运算
在这里插入图片描述

  • trans1 = postTranslate(1000, 1000);
  • trans2 = postScale(0.5f, 0.5f);
  • trans3 = preTranslate(1000, 1000);
  • trans4 = preTranslate(1000, 1000);

关于Matrix的博文 一般会提到:pre 先执行 ,post 后执行 。

由于矩阵的乘法运算满足结合率 。也就是
在这里插入图片描述
先相乘是没问题的,得到一个新的 坐标矩阵
在这里插入图片描述
上述坐标(X0,Y0), 先执行位移变换,得到一个新的坐标(Xnew, Ynew),再执行缩放变换得到一个新的坐标(Xnew2 ,Ynew2)。再。。。。

结论就是 :Matrix 的pre 和 post 操作。会转换为一系列的矩阵相乘 。最后右乘 坐标矩阵 。 越靠近右边的Matrix变换 ,会先对坐标点做处理 。

这样看来,pre是右乘操作,说是 pre 先执行,也是没有毛病的。

6 Pre ,Post 实际操作的例子

1 将图片放置到屏幕中心,并放大 2倍,在顺时针旋转90度。
在这里插入图片描述

    Matrix matrix;
    Paint paint;
    Bitmap bitmap;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        matrix = new Matrix();
        paint = new Paint();
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.touxiang, null);
    }

    /***
     * 测试View 的大小和屏幕大小相同
     * 将图片以屏幕中心为缩放中心 ,先放大2倍, 再顺时针旋转 90度 。
     */
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(bitmap, matrix, paint);
        int postTranslateX = getWidth() / 2 - bitmap.getWidth() / 2;
        int postTranslateY = getHeight() / 2 - bitmap.getHeight() / 2;
        // 将图片 位移 置 屏幕中心
        matrix.postTranslate(postTranslateX, postTranslateY);
        // 以屏幕中心为旋转点和缩放点 ,不指定坐标就是,以原点为中心
        matrix.postScale(2, 2, getWidth() / 2, getHeight() / 2);
        matrix.postRotate(90, getWidth() / 2, getHeight() / 2);
        canvas.drawBitmap(bitmap, matrix, paint);
    }

7 将Matrix 运用于图表绘制

效果图如下: 这是我下一周的股票收益折线图
在这里插入图片描述

public class MatrixTestView extends View {

    public MatrixTestView(Context context) {
        super(context);
    }

    public MatrixTestView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MatrixTestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MatrixTestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    Matrix matrix;
    Paint paint;
    Bitmap bitmap;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        matrix = new Matrix();
        paint = new Paint();
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.touxiang, null);
        mapping();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        drawProfitChart(canvas);

    }

    /***
     * 绘制股票每天收益的折现图
     * 第一天 :5000
     * 第二天 :1000
     * 第三天 :3000
     * 第四天 :500
     * 第五天 :8000
     *  首先是坐标系的确立。这里我们选取 View 的 Padding 100px 为 图表的显示区域 。
     *  X 轴的坐标 最大值是 7
     *  Y 轴的坐标最大值为 10000
     *  下一步就是将 实际的 点 (0,0)(7,0)(0,10000)(1,1000) (2,3000)  映射到 Canvas 的坐标系上 。
     *
     */
    private void drawProfitChart(Canvas canvas){
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(10);
        paint.setStyle(Paint.Style.STROKE);
        // 第一步画 坐标轴 两条线
        Path path = new Path();
        path.moveTo(0,10000);
        path.lineTo(0,0);
        path.lineTo(5,0);
        path.transform(mappingMatrix);
        canvas.drawPath(path,paint);

        // 第二步绘制 折线图
        Path valuePath = new Path();
        valuePath.moveTo(1,5000);
        valuePath.lineTo(2,1000);
        valuePath.lineTo(3,3000);
        valuePath.lineTo(4,500);
        valuePath.lineTo(5,10000);
        valuePath.transform(mappingMatrix);
        Log.e("mytest"," valuePath " +valuePath.toString());
        paint.setColor(Color.RED);
        canvas.drawPath(valuePath,paint);

        // 第三步绘制点
        paint.reset();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.BLACK);
        paint.setTextSize(50);
        float[] values = new float[]{1,5000,2,1000,3,3000,4,500,5,10000};
        float[] valuesCopy = values.clone();
        // 将坐标点变换
        mappingMatrix.mapPoints(valuesCopy);

        for(int i=0;i<valuesCopy.length;i+=2){
            float x = valuesCopy[i];
            float y = valuesCopy[i + 1];
            String valueText = (int)values[i+1]+"";
            // 绘制原点
            canvas.drawCircle(x,y,20,paint);
            // 绘制文字
            canvas.drawText(valueText, x-60, y-80, paint);
        }

        // 第四步绘制坐标值 ,省略
    }

    private  Matrix mappingMatrix =  new Matrix();
    private  int paddingLeftOrRight  = 100;
    private  int paddingTopOrBottom  = 300;

    /***
     *  图表 的 X 轴范围是 7 ,Y 轴范围是 10000 。
     *  对应的 Canvas 坐标体系 。X轴范围 是  view .getWidth() - padding*2
     *  Y轴范围 是  view .getHeight() - padding*2
     *  第一步:创建缩放 操作对应的 Matrix 。将 X 和 Y 放大    (view .getWidth() - padding*2)/7   。(view .getHeight() - padding*2)/10000
     *  Canvas 坐标系的 Y轴方向是朝下的 ,如果想让图表Y 轴的方向是 朝上。 再对matrix 做翻转处理  matrix.postScale(1,-1);
     *  此时,图表的显示区域在 Canvas 的显示区域之外, 图表的Y轴坐标已经被转换成了负数 。
     *  需要向下做位移  View.getHeight() - padding 。为了绘制PaddingLeft 。 需要向左位移 Paddiing 。
     */
    private void mapping(){
        float scaleX = (float)(getWidth() - paddingLeftOrRight * 2) / 5;
        float scaleY = (float)-(getHeight() - paddingTopOrBottom * 2) / 10000;
        mappingMatrix.reset();
        mappingMatrix.postScale(scaleX, scaleY);
        mappingMatrix.postTranslate(paddingLeftOrRight, getHeight() - paddingTopOrBottom);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值