flutter: 根视图、根元素与根渲染

flutter如何建立的视图树(WidgetTree),元素树(ElementTree)及渲染树(RenderingTree),又是如何更新视图绘制视图? 这个问题太大,刚开始一切又都是陌生的,理解起来千头万绪,所以先搞清这些树的根结点的身份是非常必要的。毫无疑问,这些根节点的建立紧密的与初始化过程关联,而确定了这些根节点之后,遍历查找更新就相对清晰了,因为绘制视图无非也是对树的遍历查找更新操作。

这部分就已经从引擎层进入到了dart层,需要了解的更多的是框架相关的机制,引擎目前用不到了。

环境: flutter sdk v1.7.8+hotfix.4@stable

先不要被Element, RenderObjectElement, RenderObject, Widget,RenderObjectWidget诸多名称吓到。与安卓封装了显式的启动运行过程不同,flutter有一个明确的runApp, 这就是进行分析的方便入口。

语言机制

多继承

需要先了解一下语言层面的一个多继承机制。虽然这里用了多继承这个名词,但是需要明确dart语言在语法上还是单继承,也就是只能extends一个类,其它接口分别再以with串接。

关键字声明

与java不同,dart没有interface(准确的说是已移除)只有abstractabstract的使用与java并无二致。没有了interface如何实现多接口对象的声明?dart用的是mixin关键字,所以就方便理解而言,把mixin当作interface, on当作extends(只针对mixin类)即可。与interface不同的是mixin声明的类是可以有方法实现体和成员对象的

class A extends B implements C, D, E {}
class B {}
interface C {}
interface D {}
interface E {}

dart等同于:

class A extends B with C, D, E {}
class B {}
mixin C {}
mixin D {}
mixin E {}

继承顺序

在以上例子中假如B,C,D都有doSomeThing方法

class A extends B with C, D {
  @override
  void doSomeThing() {
    print("A");
    super.doSomeThing();
  }
}

class B {
  @override
  void doSomeThing() {
    print("B");
  }
}

mixin C on B {
  @override
  void doSomeThing() {
    print("C");
    super.doSomeThing();
  }
}

mixin D on B {
  @override
  void doSomeThing() {
    print("D");
    super.doSomeThing();
  }
}

void main() {
  A().doSomeThing();
}

那么当执行A.doSomeThing后应该是哪个调用顺序?
直接给结论:以with声明的反顺序继承
那么问题来了:如果没有C on B会发生什么?
语言机制问题可参考这篇文章

串连调用

需要了解的第2个语法特性是串连调用,可以用..操作符串连调用类的成员方法:

class F {
  String str;
  String contact(String s) {
    return str + s;
  }

  void assign(String s) {
    str = s;
  }
}

void mai() {
  F f = F()..assign("hello")..contact(' world');
  print(f.str);
}

需要明确:用了..操作符之后调用返回的就是类对象实例,不再是方法的返回值。

初始化调用

有了以上基础(用到语言特性1: mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding)就可以理清runApp入口的调用序列:

runApp
  WidgetsFlutterBinding.ensureInitialized
    WidgetsFlutterBinding()
      BindingBase()
        WidgetsBinding.initInstances
          RendererBinding.initInstances
            SemanticsBinding.initInstances
              PaintingBinding.initInstances
                SchedulerBinding.initInstances
                  ServicesBinding.initInstances
                    GestureBinding.initInstances
                      BindingBase.initInstances

这里包含了大量的数据初始化,用到一个找一个。
再看整体序列(widgets/binding.dart:786, 用到语言特性2):

runApp
  WidgetsFlutterBinding.ensureInitialized
  WidgetsBinding.attachRootWidget
  WidgetsBinding.scheduleWarmUpFrame

MyApp实例被传给了WidgetsBinding.attachRootWidget方法,于是分析其调用序列:

runApp
  WidgetsBinding.attachRootWidget
    RenderObjectToWidgetAdapter()
    RenderObjectToWidgetAdapter.attachToRenderTree
      RenderObjectToWidgetAdapter.createElement
      RenderObjectToWidgetElement<RenderBox>.assignOwner
      BuildOwner.buildScope
      RenderObjectToWidgetElement<RenderBox>.mount

需要注意RenderObjectToWidgetAdapter是一个RenderObjectWidget类型,它用构造函数child: rootWidget, 持有了外部传入的rootWidget作为它的子视图。
RenderObjectToWidgetAdapter.createElement创建的元素被赋值给了_renderViewElement_renderViewElementWidgetsBinding实例持有。

元素关联渲染

那根渲染又是何时创建的呢?继续看mount的调用序列:

RenderObjectToWidgetElement<RenderBox>.mount
  RootRenderObjectElement.mount
    RenderObjectElement.mount
      RenderObjectWidget.createRenderObject => RenderObjectToWidgetAdapter.createRenderObject

这里容易让人误导,调用createRenderObject的其实是RenderObjectElement持有的RenderObjectWidget, 而元素RenderObjectToWidgetElement正是RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this)(widgets/binding.dart:833)所创建,这里的this其实就是RenderObjectToWidgetAdapter,所以根渲染是RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;(widgets/bindings.836),可见根渲染不是在此时创建的,而是预先被赋值仅在此时返回的。

各种根节点

由此可见MyApp作为外部传入的rootWidget不是真正的根视图,真正的根视图其实是RenderObjectToWidgetAdapter, 它被RenderObjectToWidgetElement<RenderBox>持有(一个Element持有一个Widget), 而这个Element被全局WidgetsBinding实例持有,所以根元素为RenderObjectToWidgetElement<RenderBox>

RenderObjectElementmount的时机创建了一个RenderObject实例并持有,而RenderObjectToWidgetElementRenderObjectElement的子类,创建的RenderObject具体类型为RenderObjectWithChildMixin<RenderBox>,所以它才是最终的根渲染。

有了rootElement就可以找到rootWidgetrootRenderObject, 元素树,视图树与渲染树由此建立起来。

根渲染创建

回到RenderObjectToWidgetAdapter调用构造函数的地方,传入的containerRenderingBindingRenderView get renderView => _pipelineOwner.rootNode;(rendering/binding.dart:162, attachRootWidgetWidgetsBinding的方法,但 mixin WidgetsBinding on RendererBinding,所以可以引用到RenderingBinding的成员)。

那么rootRenderObject,也就是上面的RenderView, 作为RenderObjectWithChildMixin<RenderBox>的子类(class RenderView with RenderObjectWithChildMixin<RenderBox>),又是在什么时机创建的?跟踪下来正是在初始化调用中:

runApp
  WidgetsFlutterBinding.ensureInitialized
    WidgetsFlutterBinding()
      BindingBase()
        WidgetsBinding.initInstances
          RendererBinding.initInstances
            _pipelineOwner = PipelineOwner(
            RendererBinding.initRenderView
              renderView = RenderView()
                _pipelineOwner.rootNode = value;

也就是说WidgetBinding把RendererBinding(mixin WidgetBinding with RendererBinding)的renderView作为了根渲染,而它实际是_pipelineOwner.rootNode

至此,我们便知道了所有节点遍历的起点。

原文地址:https://www.cnblogs.com/lindeer/p/11296142.html

时间: 2024-10-19 14:39:34

flutter: 根视图、根元素与根渲染的相关文章

iOS 7 使用导航控制器后有关根视图高度及位置的那些事

自从iOS7 导航控制器导航控制条的 translucent属性默认为YES 后,在项目中有时候总会遇到这样或那样有关视图控制器的根视图位置引发的问题.比如在导航控制器的RootViewController里的时候,这时你会发现如果你在 - (void)viewDidLoad { NSLog(@"%f",self.view.bounds.size.height); } 你会发现值是568 而在其上添加视图的坐标y值的零点也是屏幕的最上边 但是为什么我们在根视图上添加tableView等

iOS 返回指定的根视图

//返回指定的根视图 [self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];

获取activity的根视图

Activity的根视图是什么? Activity所谓的根视图,就是Activity的最底层的View,也就是在Acitivty创建的时候setContentView的时候传入的View. 如何获取到Activity的根视图? ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); 怎样获取根视图中的子视图? View decorChild = decor.getChildAt(index);// index为位置编号

ios--appDelegate切换根视图rootViewController(最常用的场合是首次启动跳至导航ViewController)

转载自 http://blog.csdn.net/wanggsx918/article/details/25215183 1.在appDelegate中添加一个切换的方法switchRootViewController: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow all

Android减少布局层次--有关Activity根视图DecorView的思考

1 前面文章里面总结的一张图 一直觉得有关DecorView还是有些问题没有搞清楚,今天在看了一点有关SurfaceFlinger的内容以后,顿时突发奇想,想到之前的问题,之前的思考是: 虽然可以将DecorView作为Activity布局的父View,也就是只存在  DecorView---->Activity Layout两层,但是经过试验还是会存在Title Bar,或者说是现在的Action Bar,尝试如下: protected void onCreate(Bundle savedIn

Android 通过根视图取得所有子视图

LinearLayout linearLayout = (LinearLayout) findViewById(R.id.main_container); OnClickListener listener = new NewClickListener(); Logger.Logger_d("linearLayout.getChildCount:"+linearLayout.getChildCount()); 打印出所有子视图数量 for (int i = 0; i < linea

iOS PresentViewControlle后,直接返回根视图

在开发中:用[self presentViewController:VC animated:YES completion:nil];实现跳转,多次跳转后,直接返回第一个. 例如: A presentViewController BB presentViewController CC 直接回到A: 返回第一个视图核心代码如下: [self.presentingViewController.presentingViewController dismissViewControllerAnimated:

iOS 自定义一个视图作为根视图

#import "myView.h" //使用这个myview来代替控制器自带的view@property(nonatomic,retain)myView *myview; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];    if

flutter 登录后跳转到根路由

flutter 登录以后  会有返回箭头显示 因为  路由的切换导致不是路由的第一个页面,解决办法清空路由. Navigator.of(context).pushAndRemoveUntil( new MaterialPageRoute(builder: (context) => new RootScene() ), (route) => route == null); 原文地址:https://www.cnblogs.com/wupeng88/p/10395273.html