Android动态界面开发框架VirtualView使用完整教程

阅读本文大概需要20分钟

在Android动态界面开发框架Tangram使用完整教程我们学习了Tangram的使用。

在 Tangram 体系里,页面结构可以通过配置动态更新,然而业务组件是通过 Java 代码实现的,无法动态更新。VirtualView 就是为了解决业务组件的动态更新而生的,它提供了一系列基础 UI 组件和布局组件能力,通过 XML 来搭建业务组件,并将 XML 模板编译成二进制数据,然后主体框架解析二进制数据并渲染出视图。当 XML 模板数据能动态下发的时候,客户端上的业务组件视图也就能动态更新了。

要学习使用 VirtualView,需要从四个方面入手:

  • 了解 VirtualView 模板数据的格式;
  • 了解 VirtualView 的基本原理,包括从模板编译、解析、绑定数据几个主要流程;
  • 了解 VirtualView 的基本接入方式,初始化、添加自定义基础控件、添加与外部的逻辑交互等;
  • 了解 VirtualView 内置基础控件的特性,避免重复开发。

下面我们来学习如何使用VirtualView。


Android动态界面开发框架VirtualView使用完整教程_第1张图片
目录

1 VirtualView介绍

VirtualView 是 Tangram 升级过程中引入的新的组件开发技术,主要功能包括:

  • 一份模板,两端支持。
  • 提供基础的原子控件与容器控件,支持加入自定义组件。
  • 支持一种虚拟化实现控件的协议,在模板里混合使用虚拟控件和实体控件。
  • 支持在模板里编写数据绑定的表达式。
  • 支持在模板里写事件触发的逻辑表达式。
  • 提供配套的开发工具,辅助模板开发工具。

2 接入教程

其实我们在Android动态界面开发框架Tangram使用完整教程中已经接入了,这里再复述一遍,在APP的build.gradle中添加:

implementation ('com.alibaba.android:virtualview:1.4.6@aar') {
    transitive = true
}

VirtualView的最新版本号可以在这里找到:https://github.com/alibaba/Virtualview-Android/releases

3 使用步骤

VirtualView的工作流程分为3大部分:创建UI组件、创建界面模板和客户端加载界面,具体如下:


Android动态界面开发框架VirtualView使用完整教程_第2张图片
工作流程

下面按照上图的流程分步骤讲解。

3.1 创建UI组件

根据业务需求,创建所需要的UI组件,有2种创建方式:使用框架内置(封装好)的UI组件或自定义组件。

3.1.1 使用框架内置组件

VirtualView内置了各种常用的组件,包括NText、VText、NImage和FrameLayout等,类似于Android内置的TextView、ImageView和FrameLayout等。一般来说这些内置的组件就够用了。

3.1.2 自定义UI组件

若框架内置的UI组件无法满足需求,我们还可以自定义UI组件。自定义组件的方法详见http://tangram.pingguohe.net/docs/android/add-a-custom-element

3.2 创建界面模板 & 下发

3.2.1 创建XML界面模板

根据业务需求,使用XML编写模板,需要使用专门的工具virtualview_tools

首先到https://github.com/alibaba/virtualview_tools将virtualview_tools的源码下载下来,然后使用Android Studio打开这个项目,当然不用Android Studio也可以,我们也可以用SublimeText等文本编辑器进行开发。我们编写XML模板是不能在Android Studio实时预览的,但是在手机上可以,如下图:

Android动态界面开发框架VirtualView使用完整教程_第3张图片
实时预览

为支持实时预览,需要在PC上安装如下依赖:

  • java:用于编译 VirtualView;
  • python 2:用于跑 WebServer;
  • fswatch:用于监听文件修改,安装命令:brew install fswatch
  • qrencode:用于生成二维码(可选),安装命令:brew install qrencode

打开命令行,进入compiler-tools/RealtimePreview目录,目录结构如下:

.
├── compiler.jar   (VirtualView 的编译工具)
├── config.properties   (VirtualView 编译模版需要的描述文件)
├── run.sh    (主运行脚本)
├── .dir (目录配置,json数组格式,新增模板目录需要在此配置,并重新运行脚本)
└── templates (按文件夹分割存放模版)
    └── helloworld
        ├── helloworld.json   (该模版所需参数)
        ├── helloworld.out    (该模版编译后的二进制)
        ├── helloworld.xml    (该模版源文件)
        └── helloworld_QR.png (该模版 URL 供于扫码加载)

模版统一存放在 templates 目录中,且模版相关文件名必须和目录名一致。

执行 run.sh 即可开启服务,启动服务后正常情况会有如下输出:

$ ./run.sh
############# Begin Scripts #############
############# Copy All .xml files #############
############# Prebuild : templatelist.properties #############
############# Build: out files #############
compile name: helloworld path: /realtime-preview/template/helloworld.xml
############# Run HTTP Server #############
Start HTTP Server : http://127.0.0.1:7788

之后在https://github.com/alibaba/Virtualview-Android下载VirtualView源码,用Android Studio打开,找到HttpUtil.java文件,找到这一行:

public static String getHostIp() {
    return "10.0.2.2";
}

将IP改成你PC的IP地址,注意你的手机和PC要在同一网络环境下。运行APP,在“模板实时预览”里即可看到所有 templates 目录下的模版。

下面实现一个示例,在 templates 目录下新建vvtest目录,在vvtest目录下新建vvtest.xml,代码如下:




    

    

其中类似于${action}${bgColor}这种字段是用来绑定json数据的,其支持多种表达式,具体使用请见http://tangram.pingguohe.net/docs/virtualview/simple-expr

然后再新建测试数据vvtest.json,代码如下:

{
  "bgColor": "#00FF00",
  "imageUrl": "https://gw.alicdn.com/tfs/TB1yGIdkb_I8KJjy1XaXXbsxpXa-72-72.png",
  "text": "VirtualView测试",
  "action": "您点击了VirtualView"
}

执行 run.sh ,运行VirtualView APP,打开“模板实时预览”-“vvtest”,即可看到效果,如图:

Android动态界面开发框架VirtualView使用完整教程_第4张图片
image

此时如果修改XML文件或者json文件,改完稍等一下,然后点击APP上“REFRESH”按钮,就可以看到修改后的效果。

3.2.2 编译成二进制数据

使用专门的工具virtualview_tools将编写好的XML界面模板编译成二进制数据,编译后的文件的后缀名是.out

virtualview_tools项目里,打开compiler-tools/TemplateWorkSpace/目录,其包含以下几个文件/目录(或运行后产生):

文件 作用
config.properties 配置组件 ID、xml 属性对应的 value 类型
templatelist.properties 编译的模板文件列表
build 二进制文件的输出目录
template xml 的存放路径
compiler.jar java 代码编译后 jar 文件,执行 xml 的编译逻辑
buildTemplate.sh 编译执行文件

将我们编写好的模板文件vvtest.xml放到template目录下,然后按下面步骤操作:

(1)配置 templatelist.properties

格式:xmlFileName=outFileName,Version[,platform]

  • xmlFileName:标识 template 目录下需要编译的 xml 文件名,建议不带 .xml 后缀,但目前官方做了兼容;
  • outFileName:输出到 build 目录下的 .out 文件名,也是模板名;
  • Version:表示 xml 编译后的版本号;
  • platform:同时兼容 iOS 和 android 时不写,可填的值为 android 和iphone。

那么我们在templatelist.properties文件里添加下面一行,注意outFileName的大小写很重要,后面注册模板名的时候要用到:

vvtest=VVTest,1

其实这个文件里的其它内容可以删掉,然后template目录下对应的xml文件也可以删掉,因为这些都是示例文件,我们并不需要。

(2)配置 config.properties

这个文件只有在自定义View和自定义属性的时候使用,我们暂且先忽略。

  • VIEW_ID_XXXX
    • 配置 xml 节点 id
    • 如配置 VIEW_ID_FrameLayout=1,则 xml 节点中的 在编译后会用数值1代替
    • 节点配置以 VIEW_ID_ 开头 - 自定义控件 id 从 1000 开始分配,前面 1000 保留给系统使用
  • property=ValueType
    • 配置属性值的类型,配置对所有模板生效,不支持在 1.xml 和 2.xml 中对相同的属性用不同的 ValueType 解析
    • 目前已经支持
      • 常规类型:String(默认,不需要配置)、Float、Color、Expr、Number、Int、Bool
      • 特殊类型:Flag、Type、Align、LayoutWidthHeight、TextStyle、DataMode、Visibility
      • 枚举类型:Enum
        • 枚举说明:
          • 如配置 flexDirection=Enum
          • 在解析属性是配置 row 直接转化成 int:0row-reverse转成 int:1
  • DEFAULT_PROPERTY_XXXX
    • 为了兼容就模板的编译,写的强制在二进制中写入一些属性类型定义,可以忽略

(3)构建产物

打开命令行,执行 sh buildTemplate.sh,模板编译后的文件会输出到 build 路径下,其包含以下几个目录:

  • out目录:XML 模板编译成二进制数据的文件,其他内容都是以此为基础生成,上传到 cdn,通过模板管理后台下发的也是这里的文件;
  • java目录:XML 模板编译成二进制数据之后的 Java 字节数组形式,可以直接拷贝到 Android 开发工程里使用,作为打底数据;
  • sign目录:out 格式文件的 md5 码,供模板管理平台下发模板给客户端校验使用;
  • txt目录:XML 模板编译成二进制数据之后的十六进制字符串形式,转换成二进制数据就是 java 目录下的字节数组。

在我们的示例中,分别生成了VVTEST.javaVVTest.outVVTest.md5VVTest.txt

(4)接口模式

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

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

3.2.3 模板数据下发到客户端

即客户端获取编译后的二进制数据,获取有2种途径:

  • 直接将编译后的模板打包到客户端里,开发者通过代码加载;
  • 框架先发布到模板管理后台,客户端在线更新到模板数据(即实现了动态更新)。

我们将VVTest.out文件放到assets目录下,假设它就是后端下载的文件。再把VVTEST.java放到我们代码里,之后我们也可以使用它。

3.3 客户端加载界面

客户端获取到编译后的界面模板后,进行加载与解析,最终渲染出视图界面。这个时候需要通过Java代码来使用VirtualView,我们可以单独使用VirtualView,也可以在Tangram中使用VirtualView。

3.3.1 单独使用 VirtualView

首先构建一个 VafContext 对象,代码如下:

VafContext vafContext = new VafContext(mContext.getApplicationContext());

如果使用内置的基础图片组件 NImage 和 VImage,那么需要初始化一下图片加载器,我们使用的Glide,代码如下:

vafContext.setImageLoaderAdapter(new ImageLoader.IImageLoaderAdapter() {
    @Override
    public void bindImage(String uri, ImageBase imageBase, int reqWidth, int reqHeight) {
        if (Utils.isValidContextForGlide(VirtualViewActivity.this)) {
            RequestBuilder requestBuilder =
                    Glide.with(VirtualViewActivity.this).asBitmap().load(uri);
            if (reqWidth > 0 || reqHeight > 0) {
                requestBuilder.submit(reqWidth, reqHeight);
            }
            ImageTarget imageTarget = new ImageTarget(imageBase);
            requestBuilder.into(imageTarget);
        }
    }

    @Override
    public void getBitmap(String uri, int reqWidth, int reqHeight,
                          ImageLoader.Listener lis) {
        if (Utils.isValidContextForGlide(VirtualViewActivity.this)) {
            RequestBuilder requestBuilder =
                    Glide.with(VirtualViewActivity.this).asBitmap().load(uri);
            if (reqWidth > 0 || reqHeight > 0) {
                requestBuilder.submit(reqWidth, reqHeight);
            }
            ImageTarget imageTarget = new ImageTarget(lis);
            requestBuilder.into(imageTarget);
        }
    }
});

其中ImageTarget的代码如下:

public class ImageTarget extends SimpleTarget {
    ImageBase mImageBase;
    ImageLoader.Listener mListener;

    public ImageTarget(ImageBase imageBase) {
        mImageBase = imageBase;
    }

    public ImageTarget(ImageLoader.Listener listener) {
        mListener = listener;
    }

    @Override
    public void onResourceReady(@NonNull Bitmap resource,
                                @Nullable Transition transition) {
        mImageBase.setBitmap(resource, true);
        if (mListener != null) {
            mListener.onImageLoadSuccess(resource);
        }
    }

    @Override
    public void onLoadFailed(@Nullable Drawable errorDrawable) {
        if (mListener != null) {
            mListener.onImageLoadFailed();
        }
    }
}

初始化 ViewManager 对象,代码如下:

ViewManager viewManager = vafContext.getViewManager();
viewManager.init(mContext.getApplicationContext());

加载模板数据,利用 VirtualView Tools 编译出的二进制文件,在初始化的时候加载,有两种方式,一种是直接加载二进制字节数组:

viewManager.loadBinBufferSync(VVTEST.BIN);

另一种是通过二进制文件路径加载(不推荐):

viewManager.loadBinFileSync("file:///android_asset/VVTest.out");

如果开发了自定义的基础组件,注册自定义组件的构造器:(开发自定义组件的说明参考这里)

viewManager.getViewFactory().registerBuilder(BizCommon.TM_PRICE_TEXTVIEW, new TMPriceView.Builder());
viewManager.getViewFactory().registerBuilder(BizCommon.TM_TOTAL_CONTAINER, new TotalContainer.Builder());

注册事件处理器,比如常用的点击、曝光处理:(更多事件处理信息的说明参考这里)

vafContext.getEventManager().register(EventManager.TYPE_Click, new IEventProcessor() {

    @Override
    public boolean process(EventData data) {
        Toast.makeText(VirtualViewActivity.this, data.mVB.getAction(), Toast.LENGTH_SHORT).show();
        return true;
    }
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() {

    @Override
    public boolean process(EventData data) {
        Log.d(TAG, "Exposure process: " + data.mVB.getViewCache().getComponentData());
        return true;
    }
});

以上步骤也可以根据业务需求放到自定义的Application的onCreate()方法里。

然后通过组件名参数 name 生成组件实例,注意组件名(也就是模板名)的大小写,代码如下:

View container = vafContext.getContainerService().getContainer("VVTest", true);
mLinearLayout.addView(container);

如果组件模板里写了数据绑定的表达式,那么需要给组件绑定真实的数据。假设我们之前写的vvtest.json是真实的数据,我们把这个文件复制到assets目录下,然后添加如下代码:

IContainer iContainer = (IContainer) container;
JSONObject jsonObject = Utils.getJSONDataFromAsset(this, "vvtest.json");
if (jsonObject != null) {
    iContainer.getVirtualView().setVData(jsonObject);
}

运行APP,效果如下:


Android动态界面开发框架VirtualView使用完整教程_第5张图片
image

此时点击组件还可以看到弹出的Toast。

3.3.2 在 Tangram 中使用 VirtualView

在 Tangram 里使用 VirtualView 的时候,大致流程如上述所示,只不过很多步骤已经内置到 Tangram 的初始化里了,我们只需要注册业务组件类型、加载模板数据和提供事件处理器即可。

注册 VirtualView 版本的 Tangram 组件,只需要提供组件类型名称即可,代码如下:

builder.registerVirtualView("VVTest");

在 TangramEngine 构建出来之后加载模板数据,有两种方式,代码如下:

mEngine.setVirtualViewTemplate(VVTEST.BIN);
mEngine.setVirtualViewTemplate(Utils.getAssertsFile(this, "VVTest.out"));

同样的有必要的话需要注册自定义基础组件的构造器,代码如下:

ViewManager viewManager = tangramEngine.getService(ViewManager.class);
viewManager.getViewFactory().registerBuilder(BizCommon.TM_PRICE_TEXTVIEW, new TMPriceView.Builder());

注册事件处理器,代码如下:

VafContext vafContext = mEngine.getService(VafContext.class);
vafContext.getEventManager().register(EventManager.TYPE_Click, new IEventProcessor() {
    @Override
    public boolean process(EventData data) {
        Toast.makeText(TangramActivity.this, data.mVB.getAction(), Toast.LENGTH_SHORT).show();
        return true;
    }
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() {
    @Override
    public boolean process(EventData data) {
        Log.d(TAG, "Exposure process: " + data.mVB.getViewCache().getComponentData());
        return true;
    }
});

如果使用内置的基础图片组件 NImage 和 VImage,那么需要初始化一下图片加载器,我们使用的Glide,代码如下:

vafContext.setImageLoaderAdapter(new ImageLoader.IImageLoaderAdapter() {
    @Override
    public void bindImage(String uri, ImageBase imageBase, int reqWidth, int reqHeight) {
        if (Utils.isValidContextForGlide(TangramActivity.this)) {
            RequestBuilder requestBuilder =
                    Glide.with(TangramActivity.this).asBitmap().load(uri);
            if (reqWidth > 0 || reqHeight > 0) {
                requestBuilder.submit(reqWidth, reqHeight);
            }
            ImageTarget imageTarget = new ImageTarget(imageBase);
            requestBuilder.into(imageTarget);
        }
    }

    @Override
    public void getBitmap(String uri, int reqWidth, int reqHeight,
                          ImageLoader.Listener lis) {
        if (Utils.isValidContextForGlide(TangramActivity.this)) {
            RequestBuilder requestBuilder =
                    Glide.with(TangramActivity.this).asBitmap().load(uri);
            if (reqWidth > 0 || reqHeight > 0) {
                requestBuilder.submit(reqWidth, reqHeight);
            }
            ImageTarget imageTarget = new ImageTarget(lis);
            requestBuilder.into(imageTarget);
        }
    }
});

为了演示效果,我们在Android动态界面开发框架Tangram使用完整教程的demo里的data.json追加如下代码:

{
  "type": "container-oneColumn",
  "items": [
    {
      "type": "VVTest",
      "bgColor": "#FF0000",
      "imageUrl": "https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png",
      "text": "VirtualView测试1",
      "action": "您点击了VirtualView1"
    },
    {
      "type": "VVTest",
      "bgColor": "#00FF00",
      "imageUrl": "https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png",
      "text": "VirtualView测试2",
      "action": "您点击了VirtualView2"
    },
    {
      "type": "VVTest",
      "bgColor": "#0000FF",
      "imageUrl": "https://gw.alicdn.com/tfs/TB1vqF.PpXXXXaRaXXXXXXXXXXX-110-72.png",
      "text": "VirtualView测试3",
      "action": "您点击了VirtualView3"
    }
  ]
}

运行APP,效果如下:


Android动态界面开发框架VirtualView使用完整教程_第6张图片
image

4 控件

控件分为虚拟view与实体view:

  • 虚拟view,即view的内容直接通过宿主的canvas绘制出来,它本身并不需要一个实体的view存在,在真实的view
    tree中,是看不到这个实例,只能看到其宿主的存在。它包括:原子虚拟view组件,比如文本、图片、线条;布局虚拟view组件,比如线性布局、帧布局等。虚拟view也遵循Android绘制view的逻辑,需要响应measure、layout、draw的过程才能显示。框架内置的虚拟view一般以V开头,例如VText。
  • 实体view,即原生的native view。框架内置的实体view一般以N开头,例如NText。

每个控件有公共的属性和自己的属性,如下。

4.1 公共属性

4.1.1 属性

名称 类型 默认值 描述
id int 0 组件id
layoutWidth int/float/enum(match_parent/wrap_content) 0 组件的布局宽度,与Android里的概念类似,写绝对值的时候表示绝对宽高,match_parent表示尽可能撑满父容器提供的宽高,wrap_content表示根据自身内容的宽高来布局
layoutHeight int/float/enum(match_parent/wrap_content) 0 组件的布局宽度,与Android里的概念类似,写绝对值的时候表示绝对宽高,match_parent表示尽可能撑满父容器提供的宽高,wrap_content表示根据自身内容的宽高来布局
layoutGravity enum(left/right/top/bottom/v_center/h_center) left|top 描述组件在容器中的对齐方式,left:靠左,right:靠右,top:靠上,bottom:靠底,v_center:垂直方向居中,h_center:水平方向居中,可用或组合描述
autoDimX int/float 1 组件宽高比计算的横向值
autoDimY int/float 1 组件宽高比计算的竖向值
autoDimDirection enum(X/Y/NONE) NONE 组件在布局中的基准方向,用于计算组件的宽高比,与autoDimX、autoDimY配合使用,设置了这三个属性时,在计算组件尺寸时具有更高的优先级。当autoDimDirection=X时,组件的宽度由layoutWidth和父容器决策决定,但高度 = width * (autoDimY / autoDimX),当autoDimDirection=Y时,组件的高度由layoutHeight和父容器决策决定,但宽度 = height * (autoDimX / autoDimY)
minWidth(iOS暂未支持) int/float 0 最小宽度
minHeight(iOS暂未支持) int/float 0 最小高度
padding int/float 0 同时设置 4 个内边距
paddingLeft int/float 0 左内边距,优先级高于 padding
paddingRight int/float 0 右内边距,优先级高于 padding
paddingTop int/float 0 上内边距,优先级高于 padding
paddingBottom int/float 0 下内边距,优先级高于 padding
layoutMargin int/float 0 同时设置 4 个外边距
layoutMarginLeft int/float 0 左外边距,优先级高于 layoutMargin
layoutMarginRight int/float 0 右外边距,优先级高于 layoutMargin
layoutMarginTop int/float 0 上外边距,优先级高于 layoutMargin
layoutMarginBottom int/float 0 下外边距,优先级高于 layoutMargin
background int 0 背景色
borderWidth int/float 0 边框宽度
borderColor int 0 边框颜色
borderRadius int/float 0 边框四个角的圆角半径,与 borderWidth 配合使用,支持NText、VText、VHLayout、VH2Layout、FrameLayout、GridLayout
borderTopLeftRadius int/float 0 单独设置左上角圆角半径,使用同上(iOS仅Layout支持单独设置),优先级高于 borderRadius
borderTopRightRadius int/float 0 单独设置右上角圆角半径,使用同上(iOS仅Layout支持单独设置),优先级高于 borderRadius
borderBottomLeftRadius int/float 0 单独设置左下角圆角半径,使用同上(iOS仅Layout支持单独设置),优先级高于 borderRadius
borderBottomRightRadius int/float 0 单独设置右下角圆角半径,使用同上(iOS仅Layout支持单独设置),优先级高于 borderRadius
visibility enum(visible/invisible/gone) visible 可见性,与Android里的概念类似,visible:可见,invisible:不可见,但占位,gone:不可见也不占位
dataTag string 组件数据标识
flag enum(flag_software/flag_exposure/flag_clickable/flag_longclickable/flag_touchable) 组件行为定义 flag_software:关闭view的硬件加速,flag_exposure:需要触发曝光事件,flag_clickable:需要响应点击事件,flag_longclickable:需要响应长按事件,flag_touchable:需要响应触摸事件
action string null 表示点击事件触发之后跳转到数据中action字段定义的页面
class string null 跟组件绑定的逻辑处理对象名称

4.1.2 数值单位

在组件属性里,跟尺寸相关的属性,其值的单位默认是dp,比如layoutWidth=10,表示宽度是10dp;实际值 = dp * density。

为了更精准地适配视觉,支持rp单位,表示适配屏幕大小的值,比如layoutWidth=10rp,实际值 = 10 * 屏幕宽度 / 750。

4.1.3 颜色值

在组件颜色相关属性里,支持16进制数表示,格式为#RRGGBB#AARRGGBB,也支持以下几个颜色文本:

  • black:黑色
  • blue:蓝色
  • cyan:青色
  • dkgray:深灰
  • gray:灰色
  • green:绿色
  • ltgray:浅灰
  • magenta:品红
  • red:红色
  • transparent:透明色
  • yellow:黄色

4.2 原子组件

基础元素叶子节点组件,不能嵌套其他组件。

组件名 说明 详细说明链接
NText 原生实现的文本组件,通过模板里定义可绑定以下属性:字体颜色、字号大小、字体粗细、支持文本对齐,行数,最大行数,行间距,行间距系数,截断方式。 http://tangram.pingguohe.net/docs/virtualview/ntext
VText 虚拟化实现的文本组件,精简了大量原生文本的特性,只支持以下几个特性:字体颜色、字号大小、字体粗细、支持文本对齐,只能单行显示,不支持分多行。不支持响应点击事件。 http://tangram.pingguohe.net/docs/virtualview/vtext
NImage 原生图片组件,支持加载本地图片或者网络图片,支持所有的缩放模式。 http://tangram.pingguohe.net/docs/virtualview/nimage
VImage(仅安卓) 虚拟化实现的图片组件,支持加载本地图片或者网络图片,支持基本的缩放模式。 http://tangram.pingguohe.net/docs/virtualview/vimage
NLine 实体进度条组件,支持实线、虚线,可以使用横向显示、竖向显示。 http://tangram.pingguohe.net/docs/virtualview/nline
VLine 虚拟化线条,支持实线、虚线,可以使用横向显示、竖向显示。 http://tangram.pingguohe.net/docs/virtualview/nline
VGraph(仅安卓) 虚拟化图片组件,显示圆形、矩形。 http://tangram.pingguohe.net/docs/virtualview/vgraph
Progress(仅安卓) 虚拟化进度条组件,横向显示,整体进度由背景色显示,当前进度由前景色显示,总进度为组件的宽度。 http://tangram.pingguohe.net/docs/virtualview/progress

4.3 容器组件

负责布局的容器组件,可以嵌套其他组件。

组件名 说明 详细说明链接
Scroller(仅安卓) 页面级别的容器组件,在Android上采用Recycler+StaggeredGridLayoutManager实现。通过数据驱动绑定其他组件。 http://tangram.pingguohe.net/docs/virtualview/scroller
Slider(仅安卓) 实体水平滚动的组件容器,支持内部组件的回收复用。 http://tangram.pingguohe.net/docs/virtualview/slider
Page 翻页滚动的组件,与Scroller和Slider的区别在于它是有页面效果,一页一页滚动,而Scroller、Slider是可连续滚动。 http://tangram.pingguohe.net/docs/virtualview/page
Container(仅安卓) 虚拟化的布局容器,无特殊的布局逻辑,主要是在其他虚拟组件上加一层坑,无特殊功能。支持通过数据动态创建子组件。 http://tangram.pingguohe.net/docs/virtualview/container
FrameLayout 虚拟化的帧布局,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/framelayout
RatioLayout 虚拟化的线性布局,其子组件支持写layoutRatio属性来声明在父容器空间上占用的比例,声明过layoutRatio的组件按比例分配宽或高,未声明layoutRatio的组件占用剩余的空间。不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/ratiolayout
Grid 实体网格布局容器,支持通过数据动态创建子组件。 http://tangram.pingguohe.net/docs/virtualview/grid
GridLayout 虚拟化的网格布局,与Grid的区别是它是虚拟的,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/gridlayout
VH(仅安卓) 实体的线性布局,支持通过数据动态创建子组件。 http://tangram.pingguohe.net/docs/virtualview/vh
VHLayout 虚拟化的线性布局,与VH的区别是它是虚拟的,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/vhlayout
VH2Layout 虚拟化的线性布局,与VHLayout的区别是它支持子组件分别从top、left、right、bottom四个方向进行布局。不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/vh2layout
FlexLayout(仅安卓) 虚拟化的Flex布局,Flex协议的虚拟化实现。但是只实现了部分功能,与标准的Flex布局协议还存在一些差距。不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。 http://tangram.pingguohe.net/docs/virtualview/flexlayout
NFrameLayout 实体的帧布局,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。虚拟的子组件会绘制到它的 canvas 上而不是整个大容器的 canvas,实体的子组件也会添加到它内部,因此在 borderRadius 属性的作用下会裁剪内部区域,除此之外与 FrameLayout 的功能完全一致。 http://tangram.pingguohe.net/docs/virtualview/nframelayout
NRatioLayout 实体的线性布局,其子组件支持写 layoutRatio 属性来声明在父容器空间上占用的比例,声明过layoutRatio的组件按比例分配宽或高,未声明 layoutRatio 的组件占用剩余的空间。不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。虚拟的子组件会绘制到它的 canvas 上而不是整个大容器的 canvas,实体的子组件也会添加到它内部,因此在 borderRadius 属性的作用下会裁剪内部区域,除此之外与 RatioLayout 的功能完全一致。 http://tangram.pingguohe.net/docs/virtualview/nratiolayout
NGridLayout 实体的网格布局,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。虚拟的子组件会绘制到它的 canvas 上而不是整个大容器的 canvas,实体的子组件也会添加到它内部,因此在 borderRadius 属性的作用下会裁剪内部区域,除此之外与 GridLayout 的功能完全一致。 http://tangram.pingguohe.net/docs/virtualview/ngridlayout
NVHLayout 实体的线性布局,主要提供了布局协议,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。虚拟的子组件会绘制到它的 canvas 上而不是整个大容器的 canvas,实体的子组件也会添加到它内部,因此在 borderRadius 属性的作用下会裁剪内部区域,除此之外与 VHLayout 的功能完全一致。 http://tangram.pingguohe.net/docs/virtualview/nvhlayout
NVH2Layout 实体的线性布局,不支持通过数据动态创建子组件,需要在布局模板里直接写子组件。虚拟的子组件会绘制到它的 canvas 上而不是整个大容器的 canvas,实体的子组件也会添加到它内部,因此在 borderRadius 属性的作用下会裁剪内部区域,除此之外与 VH2Layout 的功能完全一致。 http://tangram.pingguohe.net/docs/virtualview/nvh2layout

5 总结

以上介绍了VirtualView及其使用步骤和控件的属性,完整的示例代码请见:https://github.com/jimmysuncpt/TangramDemo

这里我们再总结一下:

阿里最早提出了vlayout,可以说极大地丰富了RecyclerView的功能,可以混合使用各种布局。

但是vlayout只能通过Java使用,而且只能写到客户端上,很不方便,于是提出了Tangram,该框架可以通过json来动态配置布局。

但是Tangram的组件还是需要用Java去写,能否再灵活一些呢?于是阿里又提出了VirtualView,该框架又可以通过XML来动态配置组件。

框架的思想是很好的,但是性能如何,还有待于进一步发掘。

参考链接

  1. https://github.com/alibaba/Virtualview-Android
  2. https://github.com/alibaba/virtualview_tools
  3. http://tangram.pingguohe.net/docs/virtualview/about-virtualview
  4. http://tangram.pingguohe.net/docs/android/use-virtualview
  5. https://www.jianshu.com/p/5bd7a210b800

你可能感兴趣的:(Android动态界面开发框架VirtualView使用完整教程)