Tangram集成指南-Android

Tangram集成指南

1.简介

Tangram是什么

Tangram不仅仅是一个Native(iOS & Android)的界面开发框架,而是我们从日常工作中沉淀出的一套界面解决方案,涵盖了Native SDK,GUI操作台,后端逻辑容器,组件库机制的一整套方案。

tangram组成

页面
	|
	- 布局1
	|	|
	|	- 组件11
	|	|
	|	- 组件12
	|	|
	|	- 组件13
	|
	- 布局2
	|	|
	|	- 组件21
	|	|
	|	- 组件22
	|
	- 布局3
	|	|
	|	- 组件31
	|	|
	|	- 组件32
	|
	- 组件4
	...
	|
	|
	|
	- 布局n
		|
		- 组件n1

页面:

在Android中是用RecyclerView来承载.

布局:

{
"type": "container-oneColumn", ---> 描述布局类型
"style": { ---> 描述样式
  ...
},
"header": { ---> 描述header
},
"items": [ ---> 描述组件列表
  ...
],
"footer": { ---> 描述footer
}
}

组件:

{
"type": "demo", ---> 描述组件类型
"style": { ---> 描述组件样式
  "margin": [
    10,
    10,
    10,
    10
  ],
  "height": 100,
  "width": 100
}
"imgUrl": "[URL]", ---> 业务数据
"title": "Sample"
}

Tangram解决了什么问题

面向业务、多端一致性和高性能。

局限性

Tangram的动态能力很弱,动态能力局限在组件层面,无法动态卡片.

2.gradle集成

compile 'com.alibaba.android:tangram:2.0.5'
compile ('com.alibaba.android:virtualview:1.0.5@aar') {
	transitive = true
	exclude group: 'com.android.support'
}
compile ('com.alibaba.android:vlayout:1.2.8@aar') {
	transitive = true
	exclude group: 'com.android.support'
}
compile ('com.alibaba.android:ultraviewpager:1.0.7.7@aar') {
	transitive = true
	exclude group: 'com.android.support'
}

3.tangtam使用

基本使用:

初始化 Tangram 环境
TangramBuilder.init(context, new IInnerImageSetter() {
	@Override
	public  void doLoadImageUrl(@NonNull IMAGE view,
                    @Nullable String url) {
		//假设你使用 Picasso 加载图片
		Picasso.with(context).load(url).into(view);
	}
}, ImageView.class);
初始化 TangramBuilder
TangramBuilder.InnerBuilder builder = TangramBuilder.newInnerBuilder(TangramActivity.this);
注册自定义的卡片和组件
  • 注册绑定组件类型和自定义View,比如builder.registerCell(1, TestView.class);。意思是类型为1的组件渲染时会被绑定到TestView的实例上,这种方式注册的组件使用通用的组件模型BaseCell
  • 注册绑定组件类型、自定义 model、自定义View,比如builder.registerCell(1, TestCell.class, TestView.class);。意思是类型为1的组件使用自定义的组件模型TestCell,它应当继承于BaseCell,在渲染时会被绑定到TestView的实例上。
  • 注册绑定组件类型、自定义model、自定义ViewHolder,比如builder.registerCell(1, TestCell.class, new ViewHolderCreator<>(R.layout.item_holder, TestViewHolder.class, TestView.class));。意思是类型为1的组件使用自定义的组件模型TestCell,它应当继承于BaseCell,在渲染时以R.layout.item_holder为布局创建类型为TestView的 view,并绑定到类型为TestViewHolder的 viewHolder 上,组件数据被绑定到定到TestView的实例上。
生成TangramEngine实例
TangramEngine engine = builder.build();
绑定业务 support 类到 engine
engine.register(SimpleClickSupport.class, new XXClickSupport());
engine.register(CardLoadSupport.class, new XXCardLoadSupport());
engine.register(ExposureSupport.class, new XXExposureSuport());
绑定 recyclerView
setContentView(R.layout.main_activity);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);
...
engine.bindView(recyclerView);
监听 recyclerView 的滚动事件
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
	@Override
	public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
		super.onScrolled(recyclerView, dx, dy);
		//在 scroll 事件中触发 engine 的 onScroll,内部会触发需要异步加载的卡片去提前加载数据
		engine.onScrolled();
	}
});
加载数据并传递给 engine
String json = new String(getAssertsFile(this, "data.json"));
        JSONArray data = null;
        try {
            data = new JSONArray(json);
            engine.setData(data);
        } catch (JSONException e) {
            e.printStackTrace();
        }
退出的时候销毁 engine
engine.destroy();
自定义组件:
  1. 组件开发模式一,避免了反射调用,性能上更优:

    • 实现一个自定义View,比如XXTangramView

    • 实现接口

      ITangramViewLifeCycle
      

      ,包含三个方法:

      • public void cellInited(BaseCell cell),绑定数据前调用;
      • public void postBindView(BaseCell cell),绑定数据时机;
      • public void postUnBindView(BaseCell cell),滑出屏幕,解除绑定;
    • 主要在上述public void postBindView(BaseCell cell)完成组件业务逻辑;

  2. 组件开发模式二,动态绑定数据:

    • 实现一个自定义View,比如XXTangramView

    • 必须添加三个方法,以

      @CellRender
      

      注解,功能同模式一,只是被反射调用:

      • public void cellInited(BaseCell cell)
      • public void postBindView(BaseCell cell)
      • public void postUnBindView(BaseCell cell)
    • 还可以为组件每个属性实现单独的设置方法,而不是在postBindView方法里一次性绑定数据,这些方法必须以@CellRender注解,框架会在public void postBindView(BaseCell cell)调用之前调用这些数据绑定方法;

4.VirtualView在Tangram中的使用

注册 VirtualView 版本的 Tangram 组件,只需要提供组件类型名称即可,
Tangram.Builder builder = Tangram.newBuilder(activity);
builder.registerVirtualView("tmallcomponent1");
builder.registerVirtualView("tmallcomponent2");
在 TangramEngine 构建出来之后加载模板数据,
tangramEngine.setVirtualViewTemplate(TMALLCOMPONENT1.BIN);
tangramEngine.setVirtualViewTemplate(TMALLCOMPONENT2.BIN);
...
还可以使用
tangramEngine.registerVirtualViewTemplate("tmallcomponent1",TMALLCOMPONENT1.BIN)
如何编写一个自定义基础控件
说明一下每个步骤:
  • 先编写 XML 文件,如图所示引用了一个 NText 控件;
  • 在 Config 文件里配置 NText 的标签名及属性的映射关系;像这种内置控件都已经配置好了,如果是自定义属性和控件,才要操作自己添加配置;配置文件含义参考这篇文章:VirtualView 工具大更新啦。在这个示例中,NText 标签被编译工具编译成数字 7,属性名 id、text、textSize、textColor 都被编译成一个 hashcode 索引,真正的字符串值会存储到字符串资源区;属性值 title 也是被编译成一个 hashcode 索引,真正的字符串值会存储到字符串资源区;属性值 12 被直接编译成数字 12; 属性值 #333333 被编译成颜色值 -13421773;
  • 编译工具根据 XML 文件和 Config 文件编译出一份二进制文件,交给客户端使用;
  • 客户端初始化框架的时候会根据 id 注册控件,在这个示例中 7 代表了 NativeText 类控件,它就用来实例化 XML 里的 NText 标签;
  • 最后将 XML 里 NText 下的属性传给 NativeText 实例进一步用于渲染;
创建控件实例的过程

以创建一个 PicassoImage 为例(虽然内置了 VImage 和 NImage 两个控件,但在实际业务场景中,还是使用一个自定义的图片控件比较合适,这样可以更好利用起结合图片库的内存管理、性能优化等 feature)。

目标
  • 实现一个原生 Image 控件,使用 Picasso 加载图片
  • 支持绑定 url 属性用来加载网络图片
  • 支持绑定 degree 属性用来旋转图片
1. 定义标签名及其 id,属性名及类型

在编译工具里配置文件里定义:

  • VIEW_ID_PicassoImage=1014,其中 PicassoImage 就是 XML 里的标签名,id 值为 1014,这个是自定义的,建议从 1001开始,前 1000 保留给系统使用
  • degree=Float,表示属性名是 degree ,属性值按 Float 类型编译解析;
  • url=String,表示属性名是 url,属性值按 String 类型编译,不过未在配置文件里声明的属性都是按 String 类型编译的,所以可以省略
2. 定义控件的载体 View

取名 PicassoImageView,继承 ImageView,实现 IView 接口,因为 demo 比较简单,除此之外不做其他逻辑,主要实现 IView 的接口调用对应的系统 measure、layout 方法,因为这些方法是不能在外部调用的,只能通过 IView 的接口封装一下暴露出去。

事件处理

事件处理就涉及业务逻辑了,需要外部注册处理器。也是通过 EventManager 来注册,一种类型的事件注册一个处理器就可以了,如果注册了多个,那么在事件触发的时候,会回调每一个处理器。

VafContext vafContext = tangramEngine.getService(VafContext.class);
vafContext.getEventManager().register(EventManager.TYPE_Click, new IEventProcessor() {

    @Override
    public boolean process(EventData data) {
        //handle here
        return true;
    }
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() {

    @Override
    public boolean process(EventData data) {
        //handle here
        return true;
    }
});

在处理函数里,可以获取到的资源有:

  • 触发事件的组件 model:data.mVB
  • 组件绑定的原始 JSON 数据:(JSONObject)data.mVB.getViewCache().getComponentData();

如果消化了事件,就返回 true,否则返回 false

针对不同的控件,点击处理要做不同的逻辑,一般可以用以下方式来分发处理不同的点击事件:

String action = data.mVB.getAction()

获取所点击的控件的 action 字段,它是在 XML 目标里描述的,可以是一个常量值,也可从绑定的数据到数据。建议 actio 只是一个动作描述协议,比如跳转是一个 URL 格式的地址,其他的可以是自定义的协议;然后针对不同的协议地址,注册不同的处理模块,在自定义的 IEventProcessor 里分发处理。

5.virtual_tools的使用指南

下载源码之后,可执行工具存放在目录 TemplateWorkSpace 里,包含以下几个文件/目录(或运行后产生):

文件 作用
config.properties 配置组件 ID、xml 属性对应的 value 类型
templatelist.properties 编译的模板文件列表
build 二进制文件的输出目录
template xml 的存放路径
compiler.jar java 代码编译后 jar 文件,执行 xml 的编译逻辑
buildTemplate.sh 编译执行文件
如何运行
  • 打开命令行 执行 sh buildTemplate.sh
配置 config.properties
  • VIEW_ID_XXXX
    
    • 配置 xml 节点 id
    • 如配置 VIEW_ID_FrameLayout=1,则 xml 节点中的 在编译后会用数值1代替
    • 节点配置以 VIEW_ID_ 开头 - 自定义控件 id 从 1000 开始分配,前面 1000 保留给系统使用
  • property=ValueType
    
    • 配置属性值的类型,配置对所有模板生效,不支持在 1.xml 和 2.xml 中对相同的属性用不同的 ValueType 解析

    • 目前已经支持

      • 常规类型:String(默认,不需要配置)、FloatColorExprNumberIntBool

      • 特殊类型 FlagTypeAlignLayoutWidthHeightTextStyleDataModeVisibility

      • 枚举类型

        Enum
        
        • 枚举说明:
          • 如配置 flexDirection=Enum
          • 在解析属性是配置 row 直接转化成 int:0row-reverse 转成 int:1
  • DEFAULT_PROPERTY_XXXX
    
    • 为了兼容就模板的编译,写的强制在二进制中写入一些属性类型定义,可以忽略
配置 templatelist.properties
  • 格式

    xmlFileName=outFileName,Version[,platform]
    
    • xmlFileName 标识 template 目录下需要编译的 xml 文件名建议不带 .xml 后缀,目前做了兼容
    • outFileName 输出到 build 目录下的 .out 文件名
    • Version 表示 xml 编译后的版本号
    • platform 同时兼容 iOS 和 android 时不写,可填的值为 androidiphone
xml 文件模板编写
  • 和以前的方式一样,不需要额外写 Java 代码,只需要对新增的属性在config.properties 中配置 ValueType
构建产物
  • out目录:XML 模板编译成二进制数据的文件,其他内容都是以此为基础生成,上传到 cdn,通过模板管理后台下发的也是这里的文件;
  • java目录:XML 模板编译成二进制数据之后的 Java 字节数组形式,可以直接拷贝到 Android 开发工程里使用,作为打底数据;
  • sign目录:out 格式文件的 md5 码,供模板管理平台下发模板给客户端校验使用;
  • txt目录:XML 模板编译成二进制数据之后的十六进制字符串形式,转换成二进制数据就是 java 目录下的字节数组;

接口模式

除了直接使用命令行执行工具,还可以基于此搭建完整成熟的模板工具,它可以是个客户端,也可以是个后端服务,或者是个插件,所以需要提供接口模式供宿主程序调用。

//初始化构建对象
ViewCompilerApi viewCompiler = new ViewCompilerApi();
//设置配置文件加载器,需要实现一个自己的 ConfigLoader,这里的 LocalConfigLoader 是示例
viewCompiler.setConfigLoader(new LocalConfigLoader());
//读取模板数据
FileInputStream fis = new FileInputStream(rootDir);
//调用接口,传入必备参数,参数二是模板名称类型,参数三是模板版本号,此时不区分平台,如果要区分平台,使用方单独编译即可
byte[] result = viewCompiler.compile(fis, "icon", 13);

6.通过一些思路解决开发中遇到的问题

通过自定义原子组件解决本地icon问题

一些复杂组件,使用原生书写完毕后,内置在app中

你可能感兴趣的:(android)