Tangram + Virtualview Android 初体验
前言
Tangram:阿里开源的一套动态化构建 Native 页面的框架,相较于一些H5的动态实现,它拥有天生亲和系统交互的优势。
Virtualview:同样是阿里开源的一套组件动态化方案,可以通过自定义XML方式引用这些组件来搭建UI视图。
当Tangram遇上Virtualview后,我们就可以在不重新打包的情况下,很好的实现页面的动态修改,同时页面的交互也能拥有更好的原生体验。接下来我们通过一个简单的demo来了解下它们的使用。
Git
TangramDemo:https://gitee.com/zhengdd/TangramDemo
开发使用
一、Tangram项目创建
首先我们需要创建一个TangramDemo项目,然后在module的build中做如下引用,这些都是在tangram使用时需要的一些框架。
compile 'com.alibaba.android:tangram:2.0.5@aar'
compile 'io.reactivex.rxjava2:rxjava:2.1.12'
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile('com.alibaba.android:virtualview:1.3.8@aar') {
transitive true
}
compile('com.alibaba.android:vlayout:1.2.18@aar') {
changing = true
}
compile ('com.alibaba.android:ultraviewpager:1.0.7.7@aar') {
transitive = true
}
然后我们需要在Application的onCreate()方法中进行初始化,其中图片加载,可以根据使用的框架不同进行修改,我这边使用的是Glide。
TangramBuilder.init(context, new IInnerImageSetter() {
@Override
public void doLoadImageUrl(@NonNull IMAGE view, @Nullable String url) {
Glide.with(getApplicationContext()).load(url).into(view);
}
}, ImageView.class);
二、Tangram在Activity中的使用
因为Tangram是通过RecyclerView进行动态化布局实现的,所以我们在创建activity页面布局时在需要动态化是实现的区域使用RecyclerView控件即可
然后我们需要在activity中初始化TangramBuilder并与RecyclerView想绑定。
private RecyclerView mRecyContext;
private TangramEngine engine;
private TangramBuilder.InnerBuilder builder;
builder = TangramBuilder.newInnerBuilder(context);
engine = builder.build();
//绑定view
engine.bindView(mRecyContext);
//关联滑动监听
mRecyContext.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
engine.onScrolled();
}
});
同时我们可以通过注册一些辅助业务到engine中帮助我们的进行一些逻辑处理,如点击事件,数据加载等一些处理逻辑。
engine.register(SimpleClickSupport.class, new SimpleClickSupport() {
@Override
public void setOptimizedMode(boolean optimizedMode) {
super.setOptimizedMode(optimizedMode);
}
@Override
public void onClick(View targetView, BaseCell cell, int eventType) {
super.onClick(targetView, cell, eventType);
}
@Override
public void onClick(View targetView, Cell cell, int eventType) {
super.onClick(targetView, cell, eventType);
}
@Override
public void onClick(View targetView, BaseCell cell, int eventType, Map params) {
super.onClick(targetView, cell, eventType, params);
}
@Override
public void defaultClick(View targetView, BaseCell cell, int eventType) {
super.defaultClick(targetView, cell, eventType);
}
});
engine.register(CardLoadSupport.class, new CardLoadSupport());
engine.register(ExposureSupport.class, new ExposureSupport() {
@Override
public void onExposure(@NonNull Card card, int offset, int position) {
}
});
当然,因为我们结合Virtualview使用,也需要注册一些Virtualview相关的一些逻辑服务。
private void initVirtual() {
VafContext vafContext = engine.getService(VafContext.class);
//图片加载我们可以更加框架自定义Target实现
vafContext.setImageLoaderAdapter(new IImageLoaderAdapter() {
@Override
public void bindImage(String uri, final ImageBase imageBase, int reqWidth, int reqHeight) {
GlideVVSimpleTarget target = new GlideVVSimpleTarget(imageBase);
target.setSize(reqWidth, reqHeight);
BitmapTypeRequest bitmapTypeRequest = Glide.with(getApplicationContext())
.load(uri).asBitmap();
if (imageBase.getTag() != null) {
bitmapTypeRequest.transform(new CircleTransform(context)).into(target);
} else {
bitmapTypeRequest.into(target);
}
}
@Override
public void getBitmap(String uri, int reqWidth, int reqHeight, final Listener lis) {
GlideVVSimpleTarget target = new GlideVVSimpleTarget(lis);
target.setSize(reqWidth, reqHeight);
Glide.with(getApplicationContext())
.load(uri).asBitmap().into(target);
}
});
//注册单击事件的逻辑实现
vafContext.getEventManager().register(EventManager.TYPE_Click, new IEventProcessor() {
@Override
public boolean process(EventData data) {
//我们可以通过定义数据设置的值不同做不同处理
String action = data.mVB.getAction();
return true;
}
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() {
@Override
public boolean process(EventData data) {
//handle here
return true;
}
});
vafContext.getEventManager().register(EventManager.TYPE_LongCLick, new IEventProcessor() {
@Override
public boolean process(EventData data) {
return true;
}
});
}
三、数据加载绑定
最后我们就可以通过网络请求获取data.json数据后进行注册virtualview和绑定数据动态展现页面了。其中数据可以通过virtualview_tools进行编译生成,这个在后面会讲,生成后的数据格式为json格式,主要包含templates和data两个字段的数据。
其中templates为Virtualview自定义xml通过编译后获取的字符串,为数组类型,创建了几个xml则templates数据个数就为几个。
data则为展示数据的结构与内容。
结构如下:
{
"templates": [],
"data": {
"jsondata": [
{
"items": [
],
"style": {
},
"type": "container-oneColumn"
}
]
}
}
activity中数据绑定如下:
PreviewData data = (PreviewData) msg.getData().get("data");
for (int i = 0; i < data.templates.size(); i++) {
String vvname = name + i;
//注册布局名称,与xml文件名相同
builder.registerVirtualView(vvname);
//设置ui布局数据
engine.setVirtualViewTemplate(JSONutils.getBase64(data.templates.get(i)));
}
com.google.gson.JsonArray jsonArray = data.data.getAsJsonArray("jsondata");
String json = jsonArray.toString();
JSONArray jsdata = null;
try {
jsdata = new JSONArray(json);
//绑定动态页面结构数据并显示
engine.setData(jsdata);
engine.refresh();
} catch (JSONException e) {
e.printStackTrace();
}
动态编辑
一、VirtualView工具使用
在demo中,我们实现类似如下结构的一个布局页面。
首先我们在 virtualview_tools ▸ compiler-tools ▸ RealtimePreview ▸ templates目录下创建一个KouBeiHome目录,文件结构如下图
其中.xml文件是我们根据页面编写的ui,KouBeiHome.json则是Tangram的数据结构。
然后我们通过控制台运行sh run.sh ,成功编译后会生成data.json文件。然后可以由上文中Tangram动态获取绑定实现动态页面。
二、UI实现
关于一些VirtualView组件及属性的了解,可以看这里
在编写ui页面时候,我们需要先吧一些基础元素抽取出来。
比如结构会被重复使用,便需要抽取出来构建ui,成KouBeiHome0.xml.内容格式如下
同样其他一些重复结构也被抽取实现,如
通过KouBeiHome3和KouBeiHome2实现。
最后把整体布局通过KouBeiHome4实现,具体如下
三、数据编辑
根据Tangram组件规则与上述ui设置的字段名进行数据设置。
"items": [
{
"type": "KouBeiHome4",
"mune_list": [······],
"banner_url": "https://img.alicdn.com/tfs/TB12eXNsVzqK1RjSZSgXXcpAVXa-750-291.jpg_Q90.jpg",
"title_01": [
{
"type": "KouBeiHome3",
"item_title": "1212福利社",
"item_title_more": "更多",
"enum_visibility":"gone"
}
],
······
}
]
总结
以上就是通过Tangram 和 Virtualview 结合实现动态页面的最基础的一些使用了。
总的来说,通过VirtualView,我们实现每一个xml文件都相当于一个组件,然后我们吧这些组件动态注册到Tangram中去,这样我们就能使用这些组件构建动态构建页面了。
然后Tangram通过动态加载数据,组合这些组件并设置数据,就实现了动态加载页面。