Flutter RenderBox指南——绘制篇
本文基于1.12.13+hotfix.8版本源码分析。
0、大纲
- RenderBox的用法
- 通过RenderObjectWidget把RenderBox塞进界面
1、RenderBox
在flutter中,我们最常接触的,莫过于各种各样的widget了,但是,实际负责渲染的RenderObject是很少接触的(它们之间的关联可以看看闲鱼的这篇文章:https://www.yuque.com/xytech/flutter/tge705)。而作为一名天天向上的程序员,我们自然要去学习一下它的原理,做到知其然且知其所以然。本文会先来看看RenderBox的用法,以此抛砖引玉,便于后面继续深入flutter的绘制原理。
首先,RenderBox是RenderObject的子类,它将坐标系转换成我们熟知的笛卡尔坐标系,更便于使用。使用RenderBox进行绘制(本文暂时不讲child),我们需要做三件事:
(1)测量
第一步,我们需要确定视图大小,并赋值给父类的size属性。测量有两种情况,第一种是size由自身决定,第二种是由parent决定。
首先,由自身决定size的情况,需要在performLayout方法中完成测量,通过父类的constraints可得到满足约束的值:
@override
void performLayout() {
size = Size(
constraints.constrainWidth(200),
constraints.constrainHeight(200),
);
}
第二种情况,size由parent决定,这种情况下视图大小应该完全通过parent提供的constraints测量,不存在其它因素。这种情况下,只要parent的约束不发生变化,就不会重新测量。
这种情况需要重写sizedByParent并返回true,然后在performResize中完成测量,并且performLayout中不能对size赋值! 感兴趣的同学也可以两边都写,看看会发生什么:)。
@override
void performLayout() {}
@override
void performResize() {
size = Size(
constraints.constrainWidth(200),
constraints.constrainHeight(200),
);
}
@override
bool get sizedByParent => true;
(2)绘制
RenderBox的绘制与android原生的view绘制非常相似,同样是Paint+Canvas的组合,而且api也非常接近,会非常容易上手。
@override
void paint(PaintingContext context, Offset offset) {
Paint paint = Paint()
..color = _color
..style = PaintingStyle.fill;
context.canvas.drawRect(
Rect.fromLTRB(
0,
0,
size.width,
size.height,
),
paint);
}
这样是不是就万事大吉了呢?如果通过上面的代码进行绘制,你会发现,不管在外层怎么设置位置,绘制出来的矩形都是固定在屏幕左上角的!怎么回事?
这里就是flutter中绘制与android的最大不同:在这里绘制的坐标系是全局坐标系,即原点在屏幕左上角,而非视图左上角。
细心的同学可能已经发现,paint方法中还有一个offset参数,这就是经过parent的约束后,当前视图的偏移量,绘制时应该将它考虑进去:
@override
void paint(PaintingContext context, Offset offset) {
Paint paint = Paint()
..color = _color
..style = PaintingStyle.fill;
context.canvas.drawRect(
Rect.fromLTRB(
offset.dx,
offset.dy,
offset.dx + size.width,
offset.dy + size.height,
),
paint);
}
(3)更新
在flutter中,是由Widget的配置发生变更而引起的rebuild,而这就是我们要实现的第三步:当视图属性发生变更时,标记重新布局或重新绘制,当屏幕刷新时就会做相应的刷新。
这里涉及到两个方法:markNeedsLayout、markNeedsPaint。顾名思义,前者标记重布局,后者标记重绘。
我们需要做的,就是根据属性的影响范围,在更新属性时,调用合适的标记方法,例如color变化时调用markNeedsPaint,width变化时调用markNeedsLayout。另外,两者都需要更新的情况下,调用markNeedsLayout即可,不需要两个方法都调。
set width(double width) {
if (width != _width) {
_width = width;
markNeedsLayout();
}
}
set color(Color color) {
if (color != _color) {
_color = color;
markNeedsPaint();
}
}
2、RenderObjectWidget
(1)简介
上面讲了一大堆RenderBox的用法,但是,这玩意儿怎么用到我们熟知的Widget里面去?
按照正常流程,我们得实现一个Element和一个Widget,然后在Widget中创建Element,在Element中创建和更新RenderObject,另外还得管理一大堆状态,处理非常繁琐。所幸flutter为我们封装了这一套逻辑,即RenderObjectWidget。
相信对flutter有所了解的同学都对StatelessWidget和StatefulWidget不陌生,但其实,StatelessWidget和StatefulWidget仅负责属性、生命周期等的管理,在它们的build方法实现中都会创建RenderObjectWidget,通过它来实现与RenderObject的关联。
举个栗子,我们经常使用的Image是个StatefulWidget,对应的state的build方法中实际返回了一个RawImage对象,而这个RawImage是继承自LeafRenderObjectWidget的,这正是RenderObjectWidget的一个子类;再比如Text,它build方法中创建的RichText是继承自MultiChildRenderObjectWidget,这同样是RenderObjectWidget的一个子类。
我们再看看RenderObjectWidget顶部的注释即可明白:
RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
which wrap [RenderObject]s, which provide the actual rendering of the
application.
大概意思就是RenderObject才是实际负责渲染应用的,而RenderObjectWidget提供包装了RenderObject的配置,方便我们使用。
另外,flutter还分别实现了几个子类,进一步封装了RenderObjectWidget,它们分别是LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget。其中,LeafRenderObjectWidget是叶节点,不含子Widget;SingleChildRenderObjectWidget仅有一个child;而MultiChildRenderObjectWidget则是含有children列表。这几个子类根据child的情况分别创建了对应的Element,所以通过这几个子类,我们只需要关注RenderObject的创建和更新。
(2)用法
以最简单的LeafRenderObjectWidget为例,我们需要实现createRenderObject、updateRenderObject两个方法:
class CustomRenderWidget extends LeafRenderObjectWidget {
CustomRenderWidget({
this.width = 0,
this.height = 0,
this.color,
});
final double width;
final double height;
final Color color;
@override
RenderObject createRenderObject(BuildContext context) {
return CustomRenderBox(width, height, color);
}
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) {
CustomRenderBox renderBox = renderObject as CustomRenderBox;
renderBox
..width = width
..height = height
..color = color;
}
}
Flutter RenderBox指南——绘制篇的更多相关文章
- 【转载】 Spark性能优化指南——基础篇
转自:http://tech.meituan.com/spark-tuning-basic.html?from=timeline 前言 开发调优 调优概述 原则一:避免创建重复的RDD 原则二:尽可能 ...
- 【转】【技术博客】Spark性能优化指南——高级篇
http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...
- 【转】Spark性能优化指南——基础篇
http://mp.weixin.qq.com/s?__biz=MjM5NDMwNjMzNA==&mid=2651805828&idx=1&sn=2f413828d1fdc6a ...
- 【Todo】【读书笔记】Java多线程编程指南-设计模式篇
下了这本书<Java多线程编程指南-设计模式篇>, 还有另一本<JAVA多线程设计模式>,据说内容有重复,结合着看.
- Spark性能优化指南——基础篇(转载)
前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一.Spark的功能涵盖了大数据领域的离线批处理.SQL类处理.流式/实时计算.机器学习.图计算等各种不同类型的计算操作 ...
- Spark性能优化指南——基础篇
本文转自:http://tech.meituan.com/spark-tuning-basic.html 感谢原作者 前言 在大数据计算领域,Spark已经成为了越来越流行.越来越受欢迎的计算平台之一 ...
- Spark性能优化指南——高级篇
本文转载自:https://tech.meituan.com/spark-tuning-pro.html 美团技术点评团队) Spark性能优化指南——高级篇 李雪蕤 ·2016-05-12 14:4 ...
- Java工程师学习指南 完结篇
Java工程师学习指南 完结篇 先声明一点,文章里面不会详细到每一步怎么操作,只会提供大致的思路和方向,给大家以启发,如果真的要一步一步指导操作的话,那至少需要一本书的厚度啦. 因为笔者还只是一名在校 ...
- Java工程师学习指南 中级篇
Java工程师学习指南 中级篇 最近有很多小伙伴来问我,Java小白如何入门,如何安排学习路线,每一步应该怎么走比较好.原本我以为之前的几篇文章已经可以解决大家的问题了,其实不然,因为我写的文章都是站 ...
随机推荐
- 在Windows中使用VirtualBox安装Ubuntu
VeitualBox官网下载:https://www.virtualbox.org/wiki/Downloads 安装教程:http://dblab.xmu.edu.cn/blog/337-2/ 安装 ...
- 来自BAT大厂前端工程师的自白-怎么才能学好前端
如果说理解学好web前端是先能找到一份工作,那么你应该这样做: 1.制定好一下系统的web前端学习规划,每天定量,学完什么知识点就掌握,能自己应用,而不是能看懂,写不出来东西. 2.不要自己一个人闷头 ...
- MySql --FIND_IN_SET() 函数 (转)
例子:https://www.jianshu.com/p/b2c1ba0ba34f 举个例子来说:有个文章表里面有个type字段,他存储的是文章类型,有 1头条,2推荐,3热点,4图文 .....11 ...
- ajax---post跨域思路
ajax跨域需要加的代码 header("Access-Control-Allow-Methods:GET,POST");
- Shodan使用简述
申明 本文只做相关介绍,使用者应当严格自律,承诺遵守法律法规 Shodan,一款互联网下的可怕搜索引擎.它的可怕之处在于Shodan可以搜索各种在线的网络设备.比如:摄像头.路由器.打印机.服 ...
- C++多线程队列实现
C++多线程队列实现 C++多线程队列学习 介绍 在项目中,进行多线程队列实现是一个比较麻烦的事, 找到了一个实现比较好的多线程队列实现, 自己做了一点修改更加适应自己的项目, 记录下来, 有需要的自 ...
- mac OS 安装 Homebrew软件包管理器
Homebrew macOS 缺失的软件包的管理器 中文官网 https://brew.sh/index_zh-cn 获取安装命令 /usr/bin/ruby -e "$(curl -fsS ...
- db2 锁表
2019独角兽企业重金招聘Python工程师标准>>> 查询锁表情况 db2 => get snapshot for locks on databasename 可以看到什么表 ...
- STL部分学习总结
一.map/multimap map/multimap映射容器的元素数据是由一个Key和一个Value成的,key与映照value之间具有一一映照的关系. map/multimap容器的数据结构也采用 ...
- VScode像Codeblocks一样,不启动调试和Debug直接运行
要是配置C++ 编译环境,这边走 用了VScode童鞋,都知道,写C++是不保留窗口的,除非打上断点或者: system("pause"); 这里给大家分享一种不需要,F5或者Ct ...