Android 之 Canvas API 详解 (Part 3) Matrix 和 drawBitmapMesh

news2024/12/23 13:49:23

本节引言:

在Canvas的API文档中,我们看到这样一个方法:drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)

这个Matrix可是有大文章的,前面我们在学Paint的API中的ColorFilter中曾讲过ColorMatrix 颜色矩阵,一个4 * 5 的矩阵,我们可以通过修改矩阵值来修改色调,饱和度等! 而今天讲的这个Matrix可以结合其他API来控制图形,组件的变换。比如Canvas就提供了上面的 这个drawBitmap来实现矩阵变换的效果!下面我们来慢慢研究这个东东~

官方API文档:Matrix


1.Matrix中的几个常用的变换方法

  • setTranslate(float dx, float dy):控制Matrix进行平移
  • setRotate(float degrees, float px, float py):旋转,参数依次是:旋转角度,轴心(x,y)
  • setScale(float sx, float sy, float px, float py):缩放, 参数依次是:X,Y轴上的缩放比例;缩放的轴心
  • setSkew(float kx, float ky):倾斜(扭曲),参数依次是:X,Y轴上的缩放比例

其实和Canvas变换的方法基本一致,在为Matrix设置了上面的变换后,调用Canvas的 drawBitmap()方法调用矩阵就好~


2.Matrix使用示例:

运行效果图

代码实现

MyView.java

/**
 * Created by Jay on 2015/11/11 0011.
 */
public class MyView extends View {

    private Bitmap mBitmap;
    private Matrix matrix = new Matrix();
    private float sx = 0.0f;          //设置倾斜度
    private int width,height;         //位图宽高
    private float scale = 1.0f;       //缩放比例
    private int method = 0;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi);
        width = mBitmap.getWidth();
        height = mBitmap.getHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (method){
            case 0:
                matrix.reset();
                break;
            case 1:
                sx += 0.1;
                matrix.setSkew(sx,0);
                break;
            case 2:
                sx -= 0.1;
                matrix.setSkew(sx,0);
                break;
            case 3:
                if(scale < 2.0){
                    scale += 0.1;
                }
                matrix.setScale(scale,scale);
                break;
            case 4:
                if(scale > 0.5){
                    scale -= 0.1;
                }
                matrix.setScale(scale,scale);
                break;
        }
        //根据原始位图与Matrix创建新图片
        Bitmap bitmap = Bitmap.createBitmap(mBitmap,0,0,width,height,matrix,true);
        canvas.drawBitmap(bitmap,matrix,null);    //绘制新位图
    }

    public void setMethod(int i){
        method = i;
        postInvalidate();
    }
}

布局代码:activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/ly_bar"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="重置" />

        <Button
            android:id="@+id/btn_left"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="左倾" />

        <Button
            android:id="@+id/btn_right"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="右倾" />

        <Button
            android:id="@+id/btn_zoomin"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="放大" />

        <Button
            android:id="@+id/btn_zoomout"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="缩小" />
    </LinearLayout>


    <com.jay.canvasdemo3.MyView
        android:id="@+id/myView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/ly_bar" />

</RelativeLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btn_reset;
    private Button btn_left;
    private Button btn_right;
    private Button btn_zoomin;
    private Button btn_zoomout;
    private MyView myView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        btn_reset = (Button) findViewById(R.id.btn_reset);
        btn_left = (Button) findViewById(R.id.btn_left);
        btn_right = (Button) findViewById(R.id.btn_right);
        btn_zoomin = (Button) findViewById(R.id.btn_zoomin);
        btn_zoomout = (Button) findViewById(R.id.btn_zoomout);
        myView = (MyView) findViewById(R.id.myView);


        btn_reset.setOnClickListener(this);
        btn_left.setOnClickListener(this);
        btn_right.setOnClickListener(this);
        btn_zoomin.setOnClickListener(this);
        btn_zoomout.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_reset:
                myView.setMethod(0);
                break;
            case R.id.btn_left:
                myView.setMethod(1);
                break;
            case R.id.btn_right:
                myView.setMethod(2);
                break;
            case R.id.btn_zoomin:
                myView.setMethod(3);
                break;
            case R.id.btn_zoomout:
                myView.setMethod(4);
                break;
        }
    }
}

用法非常简单,就不解释了~


3.drawBitmapMesh扭曲图像

在API文档中还有这样一个方法: drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)

参数依次是:

bitmap:需要扭曲的原位图

meshWidth/meshHeight:在横/纵向上把原位图划分为多少格

verts:长度为(meshWidth+1)*(meshHeight+2)的数组,他记录了扭曲后的位图各顶点(网格线交点) 位置,虽然他是一个一维数组,但是实际上它记录的数据是形如(x0,y0),(x1,y1)..(xN,Yn)格式的数据, 这些数组元素控制对bitmap位图的扭曲效果

vertOffset:控制verts数组从第几个数组元素开始对bitmap进行扭曲(忽略verOffset之前数据 的扭曲效果)

代码示例

运行效果图

代码实现

/**
 * Created by Jay on 2015/11/11 0011.
 */
public class MyView extends View {

    //将水平和竖直方向上都划分为20格
    private final int WIDTH = 20;
    private final int HEIGHT = 20;
    private final int COUNT = (WIDTH + 1) * (HEIGHT + 1);  //记录该图片包含21*21个点
    private final float[] verts = new float[COUNT * 2];    //扭曲前21*21个点的坐标
    private final float[] orig = new float[COUNT * 2];    //扭曲后21*21个点的坐标
    private Bitmap mBitmap;
    private float bH,bW;


    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_wuliao);
        bH = mBitmap.getWidth();
        bW = mBitmap.getHeight();
        int index = 0;
        //初始化orig和verts数组。
        for (int y = 0; y <= HEIGHT; y++)
        {
            float fy = bH * y / HEIGHT;
            for (int x = 0; x <= WIDTH; x++)
            {
                float fx = bW * x / WIDTH;
                orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
                orig[index * 2 + 1] = verts[index * 2 + 1] = fy;
                index += 1;
            }
        }
        //设置背景色
        setBackgroundColor(Color.WHITE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts
                , 0, null, 0, null);
    }

    //工具方法,用于根据触摸事件的位置计算verts数组里各元素的值
    private void warp(float cx, float cy)
    {
        for (int i = 0; i < COUNT * 2; i += 2)
        {
            float dx = cx - orig[i + 0];
            float dy = cy - orig[i + 1];
            float dd = dx * dx + dy * dy;
            //计算每个座标点与当前点(cx、cy)之间的距离
            float d = (float)Math.sqrt(dd);
            //计算扭曲度,距离当前点(cx、cy)越远,扭曲度越小
            float pull = 80000 / ((float) (dd * d));
            //对verts数组(保存bitmap上21 * 21个点经过扭曲后的座标)重新赋值
            if (pull >= 1)
            {
                verts[i + 0] = cx;
                verts[i + 1] = cy;
            }
            else
            {
                //控制各顶点向触摸事件发生点偏移
                verts[i + 0] = orig[i + 0] + dx * pull;
                verts[i + 1] = orig[i + 1] + dy * pull;
            }
        }
        //通知View组件重绘
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        //调用warp方法根据触摸屏事件的座标点来扭曲verts数组
        warp(event.getX(), event.getY());
        return true;
    }

}

实现流程分析

首先你要弄清楚,这个verts数组存储的是什么?比如 verts[0]和verts1,这两个相邻的元素其实表示的就是我们第一个点的x坐标和y坐标! 知道这一点,你就知道为什么有21 * 21个点,以及为什么数组长度等于这个值 * 2! 初始化部分也就懂了!

接着我们再来看看根据触摸事件计算verts数组元素的值的实现: 获得触摸点的x,y坐标,拿这个值去减对应点的x,y只,计算出触摸点和每个坐标点的距离 然后计算所谓的扭曲度:80000 / ((float) (dd * d));扭曲度 >= 1的,直接让该坐标 点指向这个触摸点,< 1的,则让各个顶点向触摸点发生偏移,然后再调用invalidate()重绘~ 大概就这样~多思考思考,如果还是不理解就算了~知道有这东西就好!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/791167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 生成随机图片验证码

Python 生成随机图片验证码 在写一个Web项目的时候一般要写登录操作&#xff0c;而为了安全起见&#xff0c;现在的登录功能都会加上输入图片验证码这一功能&#xff0c;在利用Django开发Web项目的过程中&#xff0c;可以使用 Python 生成一个如下所示的图片验证码&#xff1a…

MVC与MVVM模式的区别

一、MVC Model&#xff08;模型&#xff09;&#xff1a;用于处理应用程序数据逻辑&#xff0c;负责在数据库中存取数据。处理数据的crud View&#xff08;视图&#xff09;&#xff1a;处理数据显示的部分。通常视图是依据模型数据创建的。 Controller&#xff08;控制器&…

Leetcode-每日一题【剑指 Offer 51. 数组中的逆序对】

题目 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。 示例 1: 解题思路 前置知识 分治法 设计思想&#xff1a; 将规模为n的问题分解为k个规模较小的子问题…

解析LED防蓝光灯珠技术原理

LED防蓝光灯珠能有效减少蓝光对眼睛的持续伤害&#xff0c;通过便携式光谱分析仪对比检测&#xff0c;使用LED防蓝光灯珠后&#xff0c;手机屏幕发出的蓝光强度得到了有效抑制&#xff0c;减少了有害蓝光对眼睛的伤害。LED防蓝光灯珠主要是通过将有害蓝光进行反射&#xff0c;或…

(css)AI智能问答页面布局

(css)AI智能问答页面布局 效果&#xff1a; html <!-- AI框 --><div class"chat-top"><div class"chat-main" ref"chatList"><div v-if"!chatList.length" class"no-message"><span>欢迎使…

<PrivateImplementationDetails>.ComputeStringHash 错误解决办法

严重性 代码 说明 项目 文件 行 禁止显示状态 错误 CS0119 “PrivateImplementationDetails”是一个 类型&#xff0c;这在给定的上下文中无效 G:\\_Default.cs 26 活动 用 ILSPY 或者 .NET Reflector 、dnspy 等反编译出来之后 <Privat…

专访伊士曼中国区高管赵志伟:以创新应对新能源汽车后市场变化

受访人&#xff1a;伊士曼高性能膜事业部中国区商务总监赵志伟 新能源汽车发展至规模化阶段&#xff0c;以贴膜、保养维修为主的后市场产业迎来快速崛起&#xff0c;新能源消费者在汽车贴膜、改装和养护领域也表现出比燃油车更高频的需求度。 作为一家全球特种材料公司&#x…

【iOS】KVOKVC原理

1 KVO 键值监听 1.1 KVO简介 KVO的全称是Key-Value Observing&#xff0c;俗称"键值监听"&#xff0c;可以用于监听摸个对象属性值得改变。 KVO一般通过以下三个步骤使用&#xff1a; // 1. 添加监听 [self.student1 addObserver:self forKeyPath:"age"…

Windows实现端口转发(附配置过程图文详解)

文章目录 1. 前言2. 命令提示符3. 防火墙4. netsh 命令4.1 查看已有的转发规则4.2 新增转发规则4.3 删除转发规则 5. 图解汇总6. 欢迎纠正~ 1. 前言 利用Windows端口转发&#xff0c;实现本地设备 ⬅➡ 公网主机 ⬅➡ 远端服务器 2. 命令提示符 以管理员身份打开“命令提示…

【雕爷学编程】Arduino动手做(88)---水流量传感器模块2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

2023年深圳杯数学建模C题无人机协同避障航迹规划

2023年深圳杯数学建模 C题 无人机协同避障航迹规划 原题再现&#xff1a; 平面上A、B两个无人机站分别位于半径为500 m的障碍圆两边直径的延长线上&#xff0c;A站距离圆心1 km&#xff0c;B站距离圆心3.5 km。两架无人机分别从A、B两站同时出发&#xff0c;以恒定速率10 m/s…

Codeforces Round 888 (Div. 3)

原题链接&#xff1a;Dashboard - Codeforces Round 888 (Div. 3) - Codeforces 目录 A. Escalator Conversations B. Parity Sort C. Tiles Comeback D. Prefix Permutation Sums E. Nastya and Potions F. Lisa and the Martians A. Escalator Conversations 题意&…

【Python从入门到人工智能】14个必会的Python内置函数(7)——数据格式化处理综合应用场景 (实现程序主界面)

总觉得忍一忍就会好起来。真笨&#xff0c;人家不就是觉得你会忍一忍&#xff0c;所以才这样对你吗&#xff1f;当我们凶狠地对待这个世界的时候&#xff0c;才会发现这个世界&#xff0c;突然变得温文尔雅了。——余华《在细雨中呼喊》 &#x1f3af;作者主页&#xff1a; 追光…

第二章:Learning Deep Features for Discriminative Localization ——学习用于判别定位的深度特征

0.摘要 在这项工作中&#xff0c;我们重新审视了在[13]中提出的全局平均池化层&#xff0c;并阐明了它如何明确地使卷积神经网络&#xff08;CNN&#xff09;具有出色的定位能力&#xff0c;尽管它是在图像级别标签上进行训练的。虽然这个技术之前被提出作为一种训练规范化的手…

Docker构建Nginx镜像并部署前台应用

文章目录 1. 简介2. 准备工作3. 编写Dockerfile4. 编写nginx.conf5. 构建镜像6. 查看镜像是否构建成功7. 运行容器8. 访问Web应用9. 总结 1. 简介 Docker是一个开源的容器化平台&#xff0c;它可以帮助我们快速构建、发布和运行应用程序&#xff0c;实现应用程序的环境隔离和依…

全国大学生数据统计与分析竞赛2021年【本科组】-B题:用户消费行为价值分析

目录 摘 要 1 任务背景与重述 1.1 任务背景 1.2 任务重述 2 任务分析 3 数据假设 4 任务求解 4.1 任务一&#xff1a;数据预处理 4.1.1 数据清洗 4.1.2 数据集成 4.1.3 数据变换 4.2 任务二&#xff1a;对用户城市分布情况与分布情况可视化分析 4.2.1 城市分布情况可视化分析 4…

PWM定时器精准定时实现led闪烁(S3C2440裸机开发)

文章目录 前言一、PWM定时器原理二、使用步骤总结 前言 上期和大家分享了使用PWM定时器输出周期方波驱动蜂鸣器&#xff0c;那么本期分享的内容是使用PWM定时器实现定时器的功能&#xff0c;有了上期的基础&#xff0c;这期分享的内容大家理解起来应该非常easy&#xff0c;接下…

Psyco模块能优化Python的运行速度吗

目录 什么是Psyco模块 Psyco模块有什么作用 什么时候用Psyco模块 Psyco模块能优化Python的运行速度吗 总结 什么是Psyco模块 Psyco是一个用于优化Python代码的第三方模块。它的目标是通过即时编译&#xff08;Just-In-Time Compilation&#xff09;技术来提高Python程序的…

如何把pdf转成word文档格式?分享三个好用方法!

PDF文件和Word文档是我们日常生活和工作中最常见的两种文档格式。尽管PDF以其稳定的格式和出色的跨平台兼容性受到人们的喜爱&#xff0c;但在文本编辑方面&#xff0c;Word文档更具有灵活性。因此&#xff0c;将PDF转换为Word文档的需求在我们日常生活中非常常见。这篇文章将为…

微信小程序入门教程||微信小程序 小程序宿主环境||微信小程序 小程序协同工作和发布

微信小程序 小程序宿主环境 小程序宿主环境 我们称微信客户端给小程序所提供的环境为宿主环境。小程序借助宿主环境提供的能力&#xff0c;可以完成许多普通网页无法完成的功能。 上一章中我们把小程序涉及到的文件类型阐述了一遍&#xff0c;我们结合 QuickStart 这个项目来…