IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 中 Canvas的功能 -> 正文阅读

[移动开发]Android 中 Canvas的功能

Android 中 Canvas的功能

一. 如何在一屏幕上绘图

三个概念:

  1. 需要有画布Canvas
  2. 需要有笔Paint
  3. 需要有坐标系,画笔默认是在左上角(0,0)位置绘制的。我们可以通过移动画布坐标原点的形式,实现在不同位置绘制。

1. 移动坐标原点

  简单绘制一个图

我们将画笔Canvas.translate(100,400)移动之后再进行绘制,这时画布的原点已经变化了。

2. 视图坐标系

理论上 Canvas 这张纸是没有边界的,但是我们的手机屏幕是有界的。我们可以理解为我们透过一个方形的洞(手机屏幕)看一张巨画(Canvas)。

而这里我们就又存在一个问题了,因为刚才的移动,我们是移动的原点,也就是说我们的画布是静止不动的,只是落笔点一直在变动,这就导致我们绘制的图对于用户来说是看不全的,所以我们需要进行移动 方形的洞 来查看这幅画。

举个例子,我们要查看最开始所说的画,可以通过移动 Screen框来查看这幅画,而这里又出现了一个坐标系,这一坐标系则为 视图坐标系,通过 scrollerToscrollerBy 进行移动该Screen框,正数则往正半轴,负数则往负半轴。

3. 小结

自定义控件中存在两个坐标系需要明确,用一句话总结如下:

  1. 绘图坐标系:决定我们的绘制的坐标
  2. 视图坐标系:决定我们所看到的画布范围

二、Canvas的剪刀手API

Canvas 中以 clip开头 的公有方法,用于裁剪画布的内容。裁剪之后画布之外将无法绘制, 我们抽取比较好玩的参数类型为Path的方法来分享,其余的都可以一一映射进来。

1、clipPath裁剪任意形状画布

public boolean clipPath(@NonNull Path path)

描述: 只留下 path内 的画布区域,而处于path范围之外的则不显示。

举个例子: 我们先准备好一个心形的路径Path,然后调用 clipPath 从画布中将此路径内的区域 “裁剪” 下来,最后为了我们观察,使用drawColor “染”上酒红色。

// 第一步:创建 心形路径 mPath
....省略,具体请移步demo(我会贴在末尾的)

// 第二步:从画布 canvas 裁剪下心形路径之内的区域
canvas.clipPath(mPath);

// 第三步:涂酒红色
canvas.drawColor(mBgColor);

【这个有一个网上的】 自带美感的贝塞尔曲线原理与实战

画心型路径基本是使用贝塞尔曲线的( 路径关系可以参考这个画心),路径关系如文章所示本章仅讲解和绘制相关的知识。

效果如图所示:

代码如下:

/**
 * 参考链接:https://blog.csdn.net/BigBoySunshine/article/details/53898806
 */
public class HartView extends View {

    public HartView(Context context) {
        super(context);
        init();
    }


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

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

    private void init() {
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);

    }

    private Paint mPaint = new Paint();
    private Path mPath = new Path();


    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();

        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));scrcpy
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));

            if (canvas != null)
                canvas.drawPath(mPath, mPaint);
        }

    }
}
/**
 * 参考链接:https://blog.csdn.net/BigBoySunshine/article/details/53898806
 */
public class OutsideHartView extends View {

    public OutsideHartView(Context context) {
        super(context);
        init();
    }


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

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

    private void init() {
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);

    }

    private Paint mPaint = new Paint();
    private Path mPath = new Path();


    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();

        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
 
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            canvas.clipOutPath(mPath);
        }

        canvas.drawColor(getContext().getResources().getColor(R.color.color_cd6155));
    }

2. clipRect裁剪矩形

public boolean clipRect(float left, float top, float right, float bottom)

public boolean clipRect(int left, int top, int right, int bottom)

public boolean clipRect(@NonNull Rect rect)

public boolean clipRect(@NonNull RectF rect)

3. clipOutPath裁剪保留path之外的区域

public boolean clipOutPath(@NonNull Path path)

描述: 只留下 path外 的画布区域,而处于path范围之内的则不显示。(与clipPath的作用范围正好相反)

值得注意的是,该方法只能在API26版本以上调用。 低版本我们使用下一小节介绍的方法

举个例子:

我们先准备好一个心形的路径Path,然后调用 clipOutPath 从画布中将此路径之外的区域 “裁剪” 下来,最后为了我们观察,使用 drawColor “染”上酒红色。

// 第一步:创建 心形路径 mPath 
....省略,具体请移步github 

// 第二步:从画布 canvas 裁剪下心形路径之外的区域 
canvas.clipOutPath(mPath);

// 第三步:涂酒红色 
canvas.drawColor(mBgColor);

实现效果如下:

代码如下:

/**
 * 参考链接:https://blog.csdn.net/BigBoySunshine/article/details/53898806
 */
public class OutsideHartView extends View {

    public OutsideHartView(Context context) {
        super(context);
        init();
    }


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

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

    private void init() {
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);

    }

    private Paint mPaint = new Paint();
    private Path mPath = new Path();


    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();

        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
 
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            canvas.clipOutPath(mPath);
        }

       值得注意的是,该方法只能在API26版本以上调用。 低版本我们使用下一小节介绍的方法

举个例子:

我们先准备好一个心形的路径Path,然后调用 clipOutPath 从画布中将此路径之外的区域 “裁剪” 下来,最后为了我们观察,使用 drawColor “染”上酒红色。

 canvas.drawColor(getContext().getResources().getColor(R.color.color_cd6155));
    }

此类型的方法还有以下这几个,但他们的裁剪范围均为矩形

public boolean clipOutRect(float left, float top, float right, float bottom)
public boolean clipOutRect(int left, int top, int right, int bottom)
public boolean clipOutRect(@NonNull Rect rect)
public boolean clipOutRect(@NonNull RectF rect)

4、clipPath 裁剪保留区域

clipPathclipOutPath 一样,都是对画布的裁剪,不会保留空白像素,之所以这么说是因为笔者在实际工作过程中遇到过,因为通过混色方案实现过View的压感效果,但是如果这个View在可见区域外其实保留了空白像素,这种情况混色将会出现原本圆角位置出现四个阴影直角效果的情况。

public boolean clipPath(@NonNull Path path, @NonNull Region.Op op)

描述: 在画布上进行使用 path 路径进行操作,至于其作用由 op 决定。

描述比较抽象,我们通过例子来体会。但在上例子前,我们需要先了解下 Region.Op 这个枚举类型,具体内容代码如下

public enum Op {
    // A: 为我们先裁剪的路径
    // B: 为我们后裁剪的路径

    // A形状中不同于B的部分显示出来
    DIFFERENCE(0),
    // A和B交集的形状
    INTERSECT(1),
    // A和B的全集
    UNION(2),
    // A和B的全集形状,去除交集形状之后的部分
    XOR(3),
    // B形状中不同于A的部分显示出来
    REVERSE_DIFFERENCE(4),
    // 只显示B的形状
    REPLACE(5);

	// ...省略不相关代码
}

通过源码可以知道共有六种类型。值得一提的有以下两点:

1)clipOutPath 方法中使用的类型就是 DIFFERENCE,换而言之,我们可以使用以下代码代替,解决在API26 以下无法使用的问题clipOutPath 方法的问题

clipPath(mPath, Region.Op.DIFFERENCE)

2)clipPath 方法中使用的类型就是 INTERSECT,换而言之,我们可以使用以下代码代替

clipPath(mPath, Region.Op.INTERSECT)
复制代码

举些例子:

接下来我们一个个讲解这六种类型,两次裁剪比较能体现出 Region.Op 参数的作用,所以我们接下来的例子需要使用两个路径:

1、心形路径 (下列例子中的 A

2、圆形路径 (下列例子中的 B

![](http://shubin.noip.cn:9000/blogimg/16273717161891.png">

(1)DIFFERENCE

描述: A形状中不同于B的部分显示出来

效果图: 红色即为最终裁剪留下区域

![](http://shubin.noip.cn:9000/blogimg/16273715237707.png">

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.DIFFERENCE);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);

    }

(2)INTERSECT

描述: A和B交集的形状

效果图: 红色和蓝色交界的部分即为最终裁剪留下区域

![](http://shubin.noip.cn:9000/blogimg/16273733514089.png">

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.INTERSECT);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
    }

(3)UNION

描述: A和B的全集,安卓版本大于等于28不支持 ,Android.P = 28;

效果图: 红色即为最终裁剪留下区域

效果如下:

【TODO需要补齐效果】

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.UNION);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);
    }

4)XOR

描述: A和B的全集形状,去除交集形状之后的部分 ,安卓版本大于等于28不支持 ,Android.P = 28;

效果图: 红色即为最终裁剪留下区域

img

效果如下:

【TODO需要补齐效果】

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.XOR);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);

    }

(5)REVERSE_DIFFERENCE

描述: B形状中不同于A的部分显示出来,安卓版本大于等于28不支持 ,Android.P = 28;

效果图: 红色即为最终裁剪留下区域

效果如下:

【TODO需要补齐效果】

img

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.REVERSE_DIFFERENCE);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);

    }

(6)REPLACE

描述: 只显示B的形状,安卓版本大于等于28不支持 ,Android.P = 28;

效果图: 红色即为最终裁剪留下区域

效果如下:

【TODO需要补齐效果】

img 此类

代码如下:

    @Override
    protected void onDraw(Canvas canvas) {


        int mWidth = getWidth();
        int mHeight = getHeight();


        for (int i = 0 + 180; i < 361 + 180; i++) {

            double sit = i * 2 * Math.PI / 360d;

            double x = 20 * 16 * Math.pow(Math.sin(sit), 3);
            double y = 20 * (13 * Math.cos(sit) - 5 * Math.cos(2 * sit) - 2 * Math.cos(3 * sit) - Math.cos(4 * sit));
            if (i == 180) {
                mPath.moveTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
            }
            mPath.lineTo(mWidth / 2 + (float) (x), mHeight / 2 - (float) (y));
        }

        canvas.clipPath(mPath, Region.Op.REPLACE);

        canvas.save();
        mPaint.setColor(0xaa6200EE);
        canvas.drawCircle(mWidth / 2, mHeight / 2 + 400, 400, mPaint);

    }

4. 代码实战

绘制心型水波纹

  1. 绘制心形
  2. 绘制波纹
  3. 组合
  4. 动画

效果如下:

通过画布裁剪去掉无关部分

三、理解Canvas.save(),Canvas.restore(),Canvas.restoreToCount()函数

API备注
**Canvas save **把Canvas 的信息保存,压入栈
**Canvas restore **恢复到最近的一个保存点。出栈。
**restoreToCount **恢复到特定的保存点

当前矩阵变换例如:平移translate(),缩放scale(),以及旋转rotate()等

Canvas的save()、restore()这两个方法字面意思就是保存、恢复,但为什么要保存和回复呢?不保存会怎么样?

其实可以理解为Canvas.store()就是将当前的Canvas压入栈做备份,中间可能会经过若干的矩阵变换,这些变化可能是平移、也可能是旋转、缩放等。在我们绘制完成之后只需要调用Canvas.restore()就可以将画布恢复到原来的位置。但是这个操作过程中是不会改变画布上已经绘制的图像内容的,也就是说Path、bitmap等是不受影响的。压栈记录的其实是对画布的操作。

下面我们来做一个简单的实验我们就可以理解了。

  1. 我们创建一个画布

    绘制如下Path

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(0xFF000000);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        canvas.translate(100, 100);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        canvas.translate(100, 100);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        }
    

    效果如下:

  1. 增加Canvas.save()代码,然后继续绘制发现没有任何影响

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(0xFF000000);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        canvas.translate(100, 100);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        canvas.translate(100, 100);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
    
        canvas.save();
    
        canvas.translate(100, 100);
        canvas.drawLine(0, 0, 100, 0, mPaint);
        canvas.drawLine(0, 100, 0, 0, mPaint);
        }
    

? 效果如下:

?

  1. 使用 Canvas.restore()后,然后在执行重新执行绘制
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(0xFF000000);
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(10);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);
    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);
    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);

    canvas.save();

    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);

    canvas.restore();

    canvas.translate(100, 300);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    }

绘制效果如下:

可以看到前面设置的canvas.translate(100, 100) 被还原了,可以得出结论,canvas.translate(x,y)是可以被还原的。

  1. 使用Canvas.restore()还原Canvas.rotate()Canvas.scale()
@Overridehttp://shubin.noip.cn:9000/image/image_20210723_400128987.png
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(0xFF000000);
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(10);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);
    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);
    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);

    canvas.save();

    canvas.translate(100, 100);
    canvas.drawLine(0, 0, 100, 0, mPaint);
    canvas.drawLine(0, 100, 0, 0, mPaint);

    canvas.restore();

    canvas.translate(100, 300);
    canvas.drawLine(0, 0, 100, 0, mPaint);

    mPaint.setColor(0xffff00ff);
    canvas.save();
    canvas.translate(100, 100);
    canvas.rotate(90);
    canvas.drawLine(0, 0, 50, 0, mPaint);
    canvas.drawLine(0, 50, 0, 0, mPaint);
    canvas.translate(100, 100);
    canvas.scale(2, 2);
    mPaint.setColor(0xff0000ff);
    canvas.drawLine(0, 0, 50, 0, mPaint);
    canvas.drawLine(0, 50, 0, 0, mPaint);
    canvas.restore();
    canvas.drawLine(0, 0, 200, 0, mPaint);

}

绘制效果如下:

通过这个示例可以看出,Canvas.rotate()Canvas.scale()都是可以被还原的。

5.裁剪整个画布,然后执行绘制

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(0xFF000000);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);

        mPaint.setColor(0xffff00ff);
        canvas.save();http://shubin.noip.cn:9000/image/image_20210723_400128987.png
        canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);

        canvas.translate(100, 100);
        Path path = new Path();
        path.addRect(new RectF(0, 0, 200, 200), Path.Direction.CW);
        canvas.clipPath(path);
        mPaint.setColor(0xff00ffff);
        canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
        canvas.restore();

    }

实现效果如下:

  1. 通过Canvas.restore()整个画布只后重新绘制矩形块。
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(0xFF000000);
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(10);

    mPaint.setColor(0xffff00ff);
    canvas.save();
    canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);

    canvas.translate(100, 100);
    Path path = new Path();
    path.addRect(new RectF(0, 0, 200, 200), Path.Direction.CW);
    canvas.clipPath(path);
    mPaint.setColor(0xff00ffff);
    canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
    canvas.restore();
    mPaint.setColor(0xff00ffff);
    canvas.drawRect(new Rect(0, 0, 600, 600), mPaint);
}

实现效果:

Canvas的其他功能

如下图当使用 canvas.translate(100,100)方法后canvas的坐标中心就不是左上角,而是(100,100),如图左边数第一个黑色直角就是 第一次canvas.translate(50,50)后的坐标中心。我用了一个for循环对同一个canvas(注意是同一个canvas)执行了5次canvas.translate(50,50),从图中发现canvas每次都以当前坐标中心为基础移动(50,50),如上图。但是如果遇到在for循环中对canvas执行translate后不想canvas改变坐标中心怎么办?那就在canvas translate前save,后再restore。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:46:00  更:2021-07-29 11:47:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 8:52:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码