Android自定义控件探索之旅一3(笔记)

前言:这是自定义控件探索之旅的第三篇,上一篇主要介绍了自定义ViewGroup 和 自定义View的基本概念、自定义控件的基本大纲流程图。总体来说上一篇我们画大篇幅讲述了自定义的基本流程,本篇文章主要介绍的是Canvas的基本含义和操作

Android自定义控件探索之旅一5
Android自定义控件探索之旅一4
Android自定义控件探索之旅一3
Android自定义控件探索之旅一2
Android自定义控件探索之旅一1

Canvas简介

Android自定义控件探索之旅一3(笔记)_第1张图片
canvas

为了加强对一些基本概念的记忆,我请来了有道翻译君来帮助我们强化理解一些关键的类。

Canvas:帆布,Android开发一般叫它画布,既然是画布那我们就可以在画布上面绘制各种东西。

那么,在画布上具体可以绘制那些内容?下面给大家提供一张API截图

Android自定义控件探索之旅一3(笔记)_第2张图片
image

如图所示,Canvas可以绘制颜色,形状(点、线、矩形、圆),绘制路径等等。另外,Canvas也是自定义控件很基础的类,Canvas的常见操作也是自定义控件过程经常使用到的,Canvas又有那些具体的操作?

Canvas的常见操作
Canvas常见操作-位移(translate)

translate是Canvas针对坐标系的移动。需要注意的是,位移是基于当前位置移动,而不是每次基于屏幕左上角的(0,0)点移动,同一个对象不停的位移,会基于上一个对象来累加坐标系的x、y,参考代码如下:

    //创建Canvas对象
    Canvas canvas = new Canvas();
    /**
     * canvas位移
     * translate(x,y)
     */
    canvas.translate(100,100);
    
    //画布会基于上一次来进行移动,因此现在是在300,300的位置(基于左上角的0,0拉进行计算)
    canvas.translate(200,200);


Canvas常见操作-缩放(scale)

关于缩放系统为我们提供了2个方法,下面的源码可以得知,Canvas的缩放方法实则是一个方法重载,这两个方法中前两个参数是相同的,参数代表的意思为x轴和y轴的缩放比例,第二种方法比前一种多了两个参数,这2个参数的意思是用来控制缩放中心位置。系统源码如下:

    /**
     * Preconcat the current matrix with the specified scale.
     *
     * @param sx The amount to scale in X
     * @param sy The amount to scale in Y
     */
    public void scale(float sx, float sy) {
        if (sx == 1.0f && sy == 1.0f) return;
        nScale(mNativeCanvasWrapper, sx, sy);
    }

    /**
     * Preconcat the current matrix with the specified scale.
     *
     * @param sx The amount to scale in X
     * @param sy The amount to scale in Y
     * @param px The x-coord for the pivot point (unchanged by the scale)
     * @param py The y-coord for the pivot point (unchanged by the scale)
     */
    public final void scale(float sx, float sy, float px, float py) {
        if (sx == 1.0f && sy == 1.0f) return;
        translate(px, py);
        scale(sx, sy);
        translate(-px, -py);
    }

其中,缩放比例(sx,sy)取值范围详解如下表:

取值范围(n) 说明
(-∞, -1) 先根据缩放中心放大n倍,再根据中心轴进行翻转
-1 根据缩放中心轴进行翻转
(-1, 0) 先根据缩放中心缩小到n,再根据中心轴进行翻转
0 不会显示,若sx为0,则宽度为0,不会显示,sy同理
(0, 1) 根据缩放中心缩小到n
1 没有变化
(1, +∞) 根据缩放中心放大n倍

另外,缩放的中心默认为坐标原点,而缩放中心轴就是坐标轴,下面是缩放的参考代码:

 //创建Canvas对象
    Canvas canvas = new Canvas();
    //画布缩放
    canvas.scale(0.5f,0.5f);
    //画布缩放 且翻转
    canvas.scale(-0.5f,-0.5f);
    // 画布缩放  缩放中心向右偏移了200个单位
    canvas.scale(0.5f,0.5f,200,0);
    
Canvas常见操作-旋转(rotate)

下面是Canvas旋转的系统源码:

  /**
     * Preconcat the current matrix with the specified rotation.
     *
     * @param degrees The amount to rotate, in degrees
     */
    public void rotate(float degrees) {
        if (degrees == 0.0f) return;
        nRotate(mNativeCanvasWrapper, degrees);
    }

    /**
     * Preconcat the current matrix with the specified rotation.
     *
     * @param degrees The amount to rotate, in degrees
     * @param px The x-coord for the pivot point (unchanged by the rotation)
     * @param py The y-coord for the pivot point (unchanged by the rotation)
     */
    public final void rotate(float degrees, float px, float py) {
        if (degrees == 0.0f) return;
        translate(px, py);
        rotate(degrees);
        translate(-px, -py);
    }

第一个参数,就是旋转的角度;第二种方法多出来的两个参数依旧是控制旋转中心点的(和缩放一样)。另外,默认的旋转中心依旧是坐标原点,下面是旋转的参考代码:


    //创建Canvas对象
    Canvas canvas = new Canvas();
    
    // 旋转90度  
    canvas.rotate(90);

    // 旋转90度  旋转中心向右偏移200个单位
    canvas.rotate(90,200,0);      
    
Canvas常见操作-错切(skew)

错切简单点理解就是特殊类型的线性变换。另外,关于错切的API,系统只提供了一种方法,系统源码如下:

   /**
     * Preconcat the current matrix with the specified skew.
     *
     * @param sx The amount to skew in X
     * @param sy The amount to skew in Y
     */
    public void skew(float sx, float sy) {
        if (sx == 0.0f && sy == 0.0f) return;
        nSkew(mNativeCanvasWrapper, sx, sy);
    }

参数含义:
float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值.
我们知道,tan45度的值就是1,因此下面代码就一目了然

 //创建Canvas对象
    Canvas canvas = new Canvas();
    //水平错切  45度
    canvas.skew(1,0);
    // 垂直错切  45度
    canvas.skew(0,1); 

Canvas常见操作-快照(save)和回滚(restore)

由于画布的操作是不可逆的,而且很多画布操作会影响后续的步骤,因为坐标系的移动绘制出来的实际位置不同(因为开发者可以根据坐标进行自己的逻辑操作),所以对画布的一些状态进行保存和回滚也是很必要的。

相关API 简介
save 把当前的画布的状态进行保存,然后放入特定的栈中
saveLayerXxx 新建一个图层,并放入特定的栈中
restore 把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布
restoreToCount 弹出指定位置及其以上所有的状态,并按照指定位置的状态进行恢复
getSaveCount 获取栈中内容的数量(即保存次数)

系统提供的API,有save( ),以及saveLayXxx( ),源码如下所示:

   /**
     * Saves the current matrix and clip onto a private stack.
     * 

* Subsequent calls to translate,scale,rotate,skew,concat or clipRect, * clipPath will all operate as usual, but when the balancing call to * restore() is made, those calls will be forgotten, and the settings that * existed before the save() will be reinstated. * * @return The value to pass to restoreToCount() to balance this save() */ public int save() { return nSave(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG); } /** * Based on saveFlags, can save the current matrix and clip onto a private * stack. *

Note: if possible, use the * parameter-less save(). It is simpler and faster than individually * disabling the saving of matrix or clip with this method. *

* Subsequent calls to translate,scale,rotate,skew,concat or clipRect, * clipPath will all operate as usual, but when the balancing call to * restore() is made, those calls will be forgotten, and the settings that * existed before the save() will be reinstated. * * @removed * @deprecated Use {@link #save()} instead. * @param saveFlags flag bits that specify which parts of the Canvas state * to save/restore * @return The value to pass to restoreToCount() to balance this save() */ public int save(@Saveflags int saveFlags) { return nSave(mNativeCanvasWrapper, saveFlags); }

save里面的参数,SaveFlags相关API:

名称 简介
ALL_SAVE_FLAG 默认,保存全部状态
CLIP_SAVE_FLAG 保存剪辑区
CLIP_TO_LAYER_SAVE_FLAG 把剪裁区作为图层保存
FULL_COLOR_LAYER_SAVE_FLAG 保存图层的全部色彩通道
HAS_ALPHA_LAYER_SAVE_FLAG 保存图层的alpha(不透明度)通道
MATRIX_SAVE_FLAG 保存Matrix信息( translate, rotate, scale, skew)

其中,还有一种saveLayer...

// 无图层alpha(不透明度)通道
public int saveLayer (RectF bounds, Paint paint)
public int saveLayer (RectF bounds, Paint paint, int saveFlags)
public int saveLayer (float left, float top, float right, float bottom, Paint paint)
public int saveLayer (float left, float top, float right, float bottom, Paint paint, int saveFlags)

// 有图层alpha(不透明度)通道
public int saveLayerAlpha (RectF bounds, int alpha)
public int saveLayerAlpha (RectF bounds, int alpha, int saveFlags)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha, int saveFlags)

值得注意的是:saveLayerXxx方法会让你花费更多的时间去渲染图像(图层多了相互之间叠加会导致计算量成倍增长),使用前请谨慎,如果可能,尽量避免使用。

简单说完了save( ),以及saveLayXxx( ),下面说说别的API代表的意思:
restore:
restore是状态回滚的意思,就是从栈顶取出一个状态然后根据内容进行恢复。
restoreToCount:
弹出指定位置以及以上所有状态,并根据指定位置状态进行恢复。
getSaveCount:
获取保存的次数,即状态栈中保存状态的数量,

最后,合理的使用画布操作可以帮助你用更容易理解的方式创作你想要的效果,也为解决问题提供多种解决方式,例如通过简单的旋转来可以替代完成三角函数的效果。

如果这篇文章对您有开发or学习上的些许帮助,希望各位看官留下宝贵的star,谢谢。

Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果,谢谢。

你可能感兴趣的:(Android自定义控件探索之旅一3(笔记))