Tangram系列(一)Android Tangram入门


背景

Tangram是阿里出品、用于快速实现组合布局的框架模型,在手机天猫 Android & iOS版 内广泛使用。
移动端原生App最大的弱点就是不能像Web端那样即改即用,需要等到下一个版本发布之后才能看到效果,而衍生出的如多动态方案(如RN)在动态化或性能方面都不太完美。所以产生了用于快速实现组合布局的框架模型Tangram。

Tangram基本理念是粗颗粒度组件化+灵活布局容器,重点关注高性能(页面渲染效率&组件回收复用,跨父节点组件的高效回收与复用);面向业务(组件业务化、动态化,通过布局+组件的形式搭建整个页面,而不是从基本的UI元素搭建页面);多终端一致性(一个json描述的布局可以同时在iOS端与Android端使用,且表现一致)。

整体页面由卡片组成,卡片由组件组成。在Tangram中组件模型是抽象出的最小的可复用单元,承载业务逻辑和UI展示,以尽可能小的业务单元为颗粒度,而卡片模型负责逻辑,粗颗粒化,不提供布局细节描述,只声明布局方式。

本篇会从Tangram布局框架VLayout(Android)、VirtualView初探两方面来进行初步的介绍。

VLayout(Android)

思路

针对需要在长列表下做各种形态来分配不同元素的电商页首页,传统的复用容器(ListView或RecyclerView)会出现复用性降低以及需要处理嵌套滑动的情况,所以VLayout提供了一个基于RecyclerView的自定义LayoutManger,可以实现不同布局格式混排。

RecyclerView中正常只有一种布局,如果列表需要使用不同的布局,可以通过设置不同的ItemType,提供多种ViewHolder实现。而VLayout使用了不同的思路,其Adapter是串联自多个继承RecyclerView.Adapter的Adapter来管理视图的适配和数据,同时实现缓存。

VLayout中具体布局逻辑在LayoutHelper中实现,每个Adapter和LayoutHelper负责一个区域范围内的组件,不同范围内的组件之间如果类型相同,可以在滑动过程中回收复用。

系统默认布局类型

实现原理

LayoutHelper包含了它负责的组件的位置起始区域,它们会被传递给自定义的LayoutManager。当RecyclerView开始渲染页面或者滑动时,它内部维护了一个布局状态,获取当前屏幕范围内还有多少区域是空白的,下一个要加载的View的位置是多少,然后把这些信息告诉LayoutManager去加载View做布局。我们然后交给LayoutHelper去布局,不同的LayoutHelper会按照约定的协议进行进一的自定义LayoutManager拿到这个位置之后,就反向查找对应的LayoutHelper去布局。

public void appendGroup(@Nullable List<L> cards) {
        if (cards == null || cards.size() == 0)
            return;
        createSnapshot();
        final List<LayoutHelper> helpers = new LinkedList<>(getLayoutHelpers());
        //根据组件大小进行扩容
        mCards.ensureCapacity(mCards.size() + cards.size());
        //add helper负责布局内的组件以及数据
        helpers.addAll(transformCards(cards, mData, mCards));
        setLayoutHelpers(helpers);
        diffWithSnapshot();
        //刷新展示
        notifyDataSetChanged();
    }

VirtualView初探

Tangram中已经实现了页面布局的动态化,我们可以通过配置json文件自由的布局;但还有一个局限性,json中使用的卡片或者组件的type,必须是在客户端已经实现好的才能使用;如果想要在原来的版本中新增一个type类型的组件,这是没有办法做到的,还是只能通过升级客户端来实现。于是2.0版本出现了虚拟化控件VirtualView。

虚拟化

VirtualView抽象&封装了Canvas绘制视图流程,通过使用Canvas来实现UI控件的绘制,虚拟化就是实现依赖于宿主容器而没有实际View(不同于常见的Button、TextView)。通过XML引用绘制好的UI组件,从而创建出界面模板,客户端通过解析和加载XML界面模板最终渲染出界面。

虚拟组件

不论是虚拟化组件还是原生组件,都要经过计算尺寸阶段、布局阶段、绘制阶段来定义,再加上相同的尺寸计算接口、布局接口、绘制接口,这样对于宿主容器来说,包装在内部的组件就不分虚拟化还是原生,一视同仁,暴露给外面的接口也是一样的,只要将宿主容器像普通的 View 一样添加到的视图界面上,就可以在后续的渲染过程中显示出来。如果虚拟组件使用的越多,View 的个数就越少,对于系统来说层级越扁平。以示例的组件来说,最终呈现的 View 只有宿主容器和两个图片组件,如果将图片也用虚拟化的方式实现,最终 View 只有一个宿主容器,而界面仍然保持不变。

虚拟组件

优化问题及特点

优化问题
1.动态更新UI组件:实现了模板与数据分离,使用XML描述视图然后在
端上绑定动态下发的界面模板&数据,最终渲染。
2.提高性能:通过使用Canvas绘制减少视图的层级和个数来提高布局加载
效率。

特点
1.渲染性能高:渲染出来的视图结构呈现扁平化;
2.组件热更新:通过配套XML模板更新sdk;
3.跨平台:一套XML模板,可以Android、iOS通用;
4.兼容性好:支持加载&渲染原生基础组件;
5.使用方便:内置一系列基础组件可以直接使用。

VirtualView接入

在Tangram里使用VirtualView的时候,很多步骤已经内置到 Tangram 的初始化里了,外部只需要注册业务组件类型、加载模板数据、提供事件处理器。

        //Step 1: init tangram
        TangramBuilder.init(this, new IInnerImageSetter() {
            @Override
            public <IMAGE extends ImageView> void doLoadImageUrl(@NonNull IMAGE view,
                @Nullable String url) {
                Picasso.with(BannerTestActivity.this).load(url).into(view);
            }
        }, ImageView.class);

        //Tangram.switchLog(true);
        mMainHandler = new Handler(getMainLooper());

        //Step 2: register build=in cells and cards
        builder = TangramBuilder.newInnerBuilder(this);
        //Step 3: register business cells and cards 注册组件和卡片
        builder.registerCell(1, TestView.class);
        builder.registerCell(10, SimpleImgView.class);
        builder.registerCell(2, SimpleImgView.class);
        builder.registerCell(110,
            TestViewHolderCell.class,
            new ViewHolderCreator<>(R.layout.item_holder, TestViewHolder.class, TextView.class));
        builder.registerCell(199,SingleImageView.class);
        builder.registerVirtualView("vvtest");//注册组件,只需要提供组件类型名称即可
        //Step 4: new engine
        engine = builder.build();
        engine.setVirtualViewTemplate(VVTEST.BIN);//加载模板数据
        //注册事件处理器
        engine.getService(VafContext.class).setImageLoaderAdapter(new IImageLoaderAdapter() {.../}

基础布局描述实例

基本布局描术文件是一个XML文件,并附带一个json数据文件,其中的相关数据来源都可以从数据json文件中使用表达式获取。一个XML就是一个组件,Tangram通过加载这个XML文件即可使用该XML文件所描述的组件,从而实现了动态新增组件类型的功能。


描述布局文件示例
json文件示例

参考

https://github.com/alibaba/tangram-android
https://www.sohu.com/a/122226581_505818
https://www.jianshu.com/p/48764ff8449f
https://www.jianshu.com/p/cd634106f533
http://pingguohe.net/2017/02/28/vlayout-design.html

推荐阅读更多精彩内容