Android安卓进阶技巧之自定义View系列(绘制流程)

Posted 学习Android的第1024天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android安卓进阶技巧之自定义View系列(绘制流程)相关的知识,希望对你有一定的参考价值。

前言

本文旨在让读者对整个绘制流程有个感性的认识-一个图是经过了什么样的流程最后展示到用户面前的。

在讲解流程之前,我们需要有一个模型

解释:我们画的图,就是通过 Paint(画笔) 作用到 Canvas(画板) 后,再由画板作用于手机屏幕。所以这也为后面想要对图片做放大或缩小,要先做放大或缩小的操作再做绘制做了解释(这里不理解,没关系。后面内容会重新提到)。[也就是会有些文章说到的-在onDraw() 的时候,要‘顺着逻辑想,倒着逻辑写’]


绘制流程

有了上面的基本模型后,我们接着来看绘制流程的具体过程吧。

一共有三个过程:测量过程(Measure),布局过程(Layout),绘制过程(Draw)。

不需要死记硬背,我们带着逻辑去理解即可。我们从结果出发,需要绘制一个图,我们总得知道绘制到哪里,所以就需要布局。布局,其实就是找到图的四个点(left,top,right,bottom)[其实也就是图的大小。要会变通,不一定就是要这四个点,有了起点,然后知道了图的宽高,也是一样的],这样我们才知道把这个图放在画板的具体什么位置。正因为布局过程中需要知道图的大小,所以需要在布局前,进行测量过程。那这样我们也就知道测量过程其实就是得出图的大小,只不过是要根据一些规则(父 View 的约束和本身的建议)来得出结果。所以这个整个流程就很自然的出来了。


接下来,会从 单个 ViewViewGroup 两个角度来讲一下三个过程。

测量过程

单个View,一般是由 父View 通过 child.measure() 触发子 View 的 onMeasure()

//ViewGroup 源码
protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) 
    final LayoutParams lp = child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);
   
     //看这里
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
 

然后 View 中的 measure() 方法

// View 源码
public final void measure(int widthMeasureSpec, int heightMeasureSpec) 
    boolean optical = isLayoutModeOptical(this);
    if (optical != isLayoutModeOptical(mParent)) 
        //....省略无关代码
        if (cacheIndex < 0 || sIgnoreMeasureCache) 
            // measure ourselves, this should set the measured dimension flag back
            //标记1
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
         else 
            long value = mMeasureCache.valueAt(cacheIndex);
            // Casting a long to int drops the high 32 bits, no mask needed
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        

//标记1,我们重写的方法就是这个方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 

其中 onMeasure() 方法,就是我们重写的方法,然后根据规则得出 View 的大小。[这里说到的规则,就是 MeasureSpec,本文旨在理解流程,暂时不穿插该内容。后面文章会单独讲解 ]。那在 onMeasure 得到的数据,怎么在onLayout 中使用呢?我们当然可以用一个变量存起来,但实际上,我们只需要通过 setMeasuredDimension(width,height) 就可以在 onLayout 中通过 width 和 height 获取对应的数据了。

那么,整个流程如下图所示:

ViewGroup 的测量过程,思想上也是得到自己的宽高,那主要是如何确定 ViewGroup 的宽高呢?那就是当确定了该 ViewGroup 的所有子 View 的宽高后,才知道自身的宽高。所以


布局过程

单个View - 布局过程

layout() 为布局入口,然后通过 setFrame() 后确定 View 的四个点。

而因为单个 View 在 layout() 中就已经确定了自己的位置,所以 onLayout() 是有个空实现

​ViewGroup - 布局过程

当知道了单个 View 是如何布局,那么 ViewGroup 自身定位,再进行子 View 的布局[就是,又回到了上图的步骤]


绘制过程

从源码上看,可以分为7步

主要步骤就是:绘制背景-》绘制内容本身-》绘制子View-》绘制装饰(前景,滚动条等),其中对于单个View来说,dispatchDraw(canvas) 是一个空实现

// Step 1, draw the background, if needed
drawBackground(canvas);

// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;

// Step 3, draw the content
onDraw(canvas);

// Step 4, draw the children
dispatchDraw(canvas);

// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;

// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);

// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas); 


最后附上,完整的思维导图

若对内容存在疑惑,或者质疑。欢迎评论区交流,或者私聊指教一番。感激不尽~

好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码免费领取哈~

最后

按照国际惯例,给大家分享一套十分好用的android进阶资料:《全网最全Android开发笔记》。

整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。

因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。

如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。

(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码免费领取哈~

以上是关于Android安卓进阶技巧之自定义View系列(绘制流程)的主要内容,如果未能解决你的问题,请参考以下文章

Android进阶之自定义View实战贝塞尔曲线应用

Android进阶之自定义View实战贝塞尔曲线应用

Android 自定义View之自绘控件

Android进阶之自定义View实战九宫格手势解锁实现

Android进阶之自定义View实战九宫格手势解锁实现

Android进阶之自定义View实战九宫格手势解锁实现