上一篇提到了,ios跟android的布局两端需要统一,这就需要一个标准的布局标准. Flexbox布局是w3c提出的用来进行css布局的一套新的方案。 定义了一套标准在横轴跟纵轴方向排列元素,结合下面的资源看一会就明白了。 网上有很多文章介绍语法,这里直接列出了网上的一些资源:

  1. Flex布局教程:语法篇
  2. Flex布局教程: 实例篇
  3. a-visual-guide-to-css3-flexbox-properties
  4. a-guide-to-flexbox

在Android平台上,google也有一个开源的实现,flexbox-layout,有5000多个star,看起来很流行的样子。 在框架实现的第一版中,我就采用了这个layout来作为布局引擎。在使用过程中也发现了挺多问题,尤其是在嵌套比较深的情况(作为list的ItemView时),会发现性能有点差, list滑动的时候跟用RelativeLayout实现的item比起来还是不那么流畅,虽然我自己也做了一些优化,但是考虑到其他因素(过滤器,数据绑定),最后在2.0版本中还是抛弃了google的这个实现。当然google在开发这个layout的时候,应该也不是为了完全替代其他的layout(它自己的demo中就是跟其他layout混用的),所以只能说flexbox-layout不适合我们这个场景。

在React-native和Weex中,底层的布局引擎用的都是facebook的css-layout,这是一个跨平台布局引擎,而且最重要的是它是独立的一个引擎,跟各个平台的UI系统没有一丁点关系,这样的话,我们就可以做很多优化,比如在后台线程进行预先layout,等layout完成了再切换到主线程进行渲染,这样就大大提高了性能。最近facebook用c语言重写了css-layout,在不同平台上有对应的wrapper,改名叫yoga,不仅真正实现了不同平台一样的算法,而且性能得到更一部提升。我们这个框架在2.0版本中也进行重构,底层就是采用yoga来进行布局。

yoga中最主要的一个概念就是YogaNode,一个布局就是由各个元素对应的YogaNode组成的树,通过给每个YogaNode设置相应的属性,最终调用YogaNode.calculateLayout,就能得到各个YogaNode的坐标跟宽高,而且引擎做了各种优化来尽量减少不必要的measure。下面是github上的一些测试用例,感兴趣的可以自己去github或者官网以及java api上看。

@Test
  public void testBaseline() {
    final YogaNode root = new YogaNode();
    root.setFlexDirection(YogaFlexDirection.ROW);
    root.setAlignItems(YogaAlign.BASELINE);
    root.setWidth(100);
    root.setHeight(100);

    final YogaNode child1 = new YogaNode();
    child1.setWidth(40);
    child1.setHeight(40);
    root.addChildAt(child1, 0);

    final YogaNode child2 = new YogaNode();
    child2.setWidth(40);
    child2.setHeight(40);
    child2.setBaselineFunction(new YogaBaselineFunction() {
        public float baseline(YogaNodeAPI node, float width, float height) {
          return 0;
        }
    });
    root.addChildAt(child2, 1);

    root.calculateLayout();

    assertEquals(0, (int) child1.getLayoutY());
    assertEquals(40, (int) child2.getLayoutY());
  }

  @Test
  public void testLayoutMargin() {
    final YogaNode node = new YogaNode();
    node.setWidth(100);
    node.setHeight(100);
    node.setMargin(YogaEdge.START, 1);
    node.setMargin(YogaEdge.END, 2);
    node.setMargin(YogaEdge.TOP, 3);
    node.setMargin(YogaEdge.BOTTOM, 4);
    node.calculateLayout();

    assertEquals(1, (int) node.getLayoutMargin(YogaEdge.LEFT));
    assertEquals(2, (int) node.getLayoutMargin(YogaEdge.RIGHT));
    assertEquals(3, (int) node.getLayoutMargin(YogaEdge.TOP));
    assertEquals(4, (int) node.getLayoutMargin(YogaEdge.BOTTOM));
  }

@Test
  public void testMeasure() {
    final YogaNode node = new YogaNode();
    node.setMeasureFunction(new YogaMeasureFunction() {
        public long measure(
            YogaNodeAPI node,
            float width,
            YogaMeasureMode widthMode,
            float height,
            YogaMeasureMode heightMode) {
          return YogaMeasureOutput.make(100, 100);
        }
    });
    node.calculateLayout();
    assertEquals(100, (int) node.getLayoutWidth());
    assertEquals(100, (int) node.getLayoutHeight());
  }

上一篇: 基于Flexbox的布局框架(一) 下一篇: 基于Flexbox的布局框架(三) - 异步Layout(Text, Image, List)