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

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

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

Canvas简介

canvas

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

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

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

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.
     * <p>
     * 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.
     * <p class="note"><strong>Note:</strong> if possible, use the
     * parameter-less save(). It is simpler and faster than individually
     * disabling the saving of matrix or clip with this method.
     * <p>
     * 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地址),文章请勿滥用,也希望大家尊重笔者的劳动成果,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容