一种客户端视图的绘制方法和装置与流程

文档序号:18196926发布日期:2019-07-17 05:55阅读:255来源:国知局
一种客户端视图的绘制方法和装置与流程

本申请涉及计算机技术领域,特别是涉及一种客户端视图的绘制方法和一种客户端视图的绘制装置。



背景技术:

电子商务技术的发展改变了人们的生活和消费习惯,越来越多的用户已经习惯通过网络进行消费、购物。用户可以在电商网站上选购商品,通过电子支付手段付清货款,然后便可以等待选购的商品被送至家中。

目前,用户进行网上购物可以通过pc或移动终端上的电商类app来实现。以电商类app为例,电商网站的开发人员经常需要根据一些临时性的需求对app的界面视图进行调整。但是,传统的界面开发技术只能在发布app新版本的时候才能更新界面视图,对于已经发布的应用根本无法进行动态更改。针对上述问题,开发人员开始考虑采用html5标准进行页面的开发。然而,html5对于有些场景来说太重量级了。例如,某些时候可能只需要对页面的某个局部区域进行更新,而整个页面并不是完全采用html5开发的。即使采用了html5开发整个页面,从目前的效果来看,其性能和用户体验也仍然较差。

为了解决htnl5在移动终端上app的开发过程中的各种不足,部分电商类app—例如,天猫(tmall)客户端—在其开发方案中,提供了一种tangram的技术框架来承载首页、品牌页等页面的开发。通过tangram可以做到页面结构的动态化调整与更改。但是,对于页面里组件的开发,tangram技术还是采用了原生的开发方式。由于原生组件都是按照平台特定协议来实现的,每个原生组件都包含了大量的通用实现的信息。为了通用化方案设计,比如文本组件需要考虑字体、多语言、emoji、从左往右排列或者从右往左排列等多种场景的文字显示,每个组件实例在内存中也要占用不少空间。此外,每个原生组件内置包含了大量的逻辑,在运行过程中必然会带来大量开销,但其中大部分逻辑对于产品场景而言,却是不必要的。



技术实现要素:

鉴于上述问题,提出了本申请实施例以便提供一种克服上述问题或者至少部分地解决上述问题的一种客户端视图的绘制方法和相应的一种客户端视图的绘制装置。

为了解决上述问题,本申请公开了一种客户端视图的绘制方法,包括:

获取业务组件模板,所述业务组件模板包含原始模板数据;

将所述原始模板数据编译为目标数据;

对所述目标数据进行解析,以获得相应的目标模板数据;

依据所述目标模板数据,创建业务组件;

获取匹配所述业务组件的业务数据;

将所述业务数据绑定至所述业务组件中。

为了解决上述问题,本申请公开了一种客户端视图的绘制方法,包括:

当接收到绘制客户端视图的指令时,确定各个业务组件的尺寸大小;

根据所述各个业务组件的尺寸大小,对所述各个业务组件进行排布;

对排布后的各个业务组件进行绘制。

为了解决上述问题,本申请公开了一种客户端视图的绘制装置,包括:

业务组件模板获取模块,用于获取业务组件模板,所述业务组件模板包含原始模板数据;

原始模板数据编译模块,用于将所述原始模板数据编译为目标数据;

目标数据解析模块,用于对所述目标数据进行解析,以获得相应的目标模板数据;

业务组件创建模块,用于依据所述目标模板数据,创建业务组件;

业务数据获取模块,用于获取匹配所述业务组件的业务数据;

业务数据绑定模块,用于将所述业务数据绑定至所述业务组件中。

为了解决上述问题,本申请公开了一种客户端视图的绘制装置,包括:

尺寸大小确定模块,用于当接收到绘制客户端视图的指令时,确定各个业务组件的尺寸大小;

业务组件排布模块,用于根据所述各个业务组件的尺寸大小,对所述各个业务组件进行排布;

业务组件绘制模块,用于对排布后的各个业务组件进行绘制。

与背景技术相比,本申请实施例包括以下优点:

本申请实施例,通过标准化的模型定义以及约定的渲染流程,每个基础组件都可以单独开发,并且在开发完毕之后可以自由组合,像搭积木一样搭建出复杂的视图,具备极强的通用性。业务方在使用的时候不用再采用写原生的自定义view或在canvas上绘制视图的方式来创建业务组件。即使在遇到内置的基础组件不满足相应的业务场景的时候,也只需要按照协议开发新的基础组件即可,并且开发好的基础组件也能够提供给其他业务方使用,解决了现有技术中通过直接使用canvas绘制视图不具备通用性的问题。

第二,本申请实施例提供的方案不仅能在android平台上实现,在ios平台上也能够实现,并且能够保证在android平台和ios平台上两端一致,为后续更丰富的组件体系开发打下了基础。而且,本方案也不需要依赖任何特殊的设施,在任何应用程序中均可以直接使用,解决了现有技术中采用开源框架litho进行组建的创建只能在android平台上实现且必须依赖对应厂商体系的大量周边设施的问题。

第三,基于本申请实施例提供的方案,可以开发一整套完备的组件体系。在内置组件的使用、业务组件的搭建时,均无需通过写代码的方式去实现,而是可以直接在xml模板里使用它们,然后使用特定的工具将模板编译、最后使用的时候反过来解析出组件实例并最终渲染出来即可。在整个过程中,业务方使用时可以脱离代码的限制,不用写代码也能开发出一个新的业务组件,解决了现有技术中无论采用何种方案在创建业务组件时都需要写代码的问题,提高了业务组件开发的效率。

第四,在本申请实施例中,可以采用大家熟悉的xml编写模板,对于客户端开发来说没有门槛,只需要熟悉内置组件的使用即可。在开发时,不需要学习lua语言,也不需要学习js。虽然js学起来很容易,但采用本申请实施例提供的方案开发业务组件,连js都可以不用熟悉。

第五,本申请实施例提供的方案都是按照轻量级、小而快的原则开发业务组件,没有引入重量级的框架、解析器等,能够最接近原生的代码开发,相对于weex或html5等方案具有更好的组件性能。

附图说明

图1是本申请的一种组件体系的结构框图;

图2是本申请的一种组件体系的整体架构示意图;

图3是本申请的一种客户端视图的绘制方法实施例一的步骤流程图;

图4是本申请的一种客户端视图的绘制方法实施例一的运行时序图;

图5是本申请的一种编译原始模板数据的流程图;

图6是本申请的一种序列化后的组件模板文件格式的示意图;

图7是本申请的一种解析目标数据的流程图;

图8是本申请的一种创建业务组件的流程图;

图9是本申请的一种绑定业务数据的流程图;

图10是本申请的一种客户端视图的绘制方法实施例二的步骤流程图;

图11是本申请的一种业务组件的层次示意图;

图12是本申请的一种绘制图11所示的业务组件的运行时序图;

图13是本申请的一种客户端视图的绘制装置实施例一的结构框图;

图14是本申请的一种客户端视图的绘制装置实施例二的结构框图。

具体实施方式

为使本申请的上述目的、特征和优点能够更加明显易懂,下面结合附图和具体实施方式对本申请作进一步详细的说明。

为使本领域技术人员更好地理解本申请,首先对本申请涉及的技术术语作一说明:

基础组件:也称作基础视图组件,是客户端应用程序界面上的可视元素。例如,文本、图片、线条等等。

容器组件:客户端应用程序界面上负责对基础视图组件进行布局的容器,它可以包含容器组件或者基础视图组件,并能够让子元素按照一定方式排列布局。

原生组件:在每一种视图框架平台里,都提供了上述基础视图组件和容器组件。它们是在各个平台下都有各自的实现类,有特定的实现协议与规范。在android或者ios平台上,惯常的组件开发是通过写java代码或者objective-c代码开发的。它们在客户端发布之后一般无法在线上修改,只能重新发布版本,采用这种方式开发的组件叫做原生组件。

动态组件:相对于原生组件,通过其他方式开发的,并在客户端发布之后还能进行修改的组件。例如,采用html5、weex,或者本申请提供的方案开发的组件,称之为动态组件。

虚拟化组件:除了使用原生组件创建视图,一般平台都提供一种利用canvas绘制界面的功能。在本申请提供的方案中,引入了一种计算尺寸的协议,定义了尺寸计算、布局、绘制三个流程的规范,进而在canvas上绘制界面。通过这种方式创建的基础视图组件或者容器组件称为虚拟化组件。虚拟化组件只需依托一个原生容器组件作为宿主容器,并能够承重其他虚拟化组件的展示。

混合组件:原生组件和虚拟化组件通过遵循共同的协议,将它们混合使用,使得原生组件里包含虚拟化组件,虚拟化组件里包含原生组件,采用这样的模式创建的组件称为混合组件。

luaview:一种开源的跨平台移动界面开发工具,通过lua作为脚本预演编写界面和逻辑,最终渲染出native界面,能够支持ios和android等操作系统。

weex:一种开源的跨平台移动开发工具,能够兼顾性能与动态性,让移动开发者通过简捷的前端语法写出native级别的性能体验,并支持ios、android、yunos及web等多端部署。

reactnative:一种开源的跨平台移动开发框架,与weex类似,采用前端语法编写界面并渲染出native界面,能够支持ios和android等操作系统。

html:超文本标记语言,一种用来创建网页的文档格式。

tangram:一种在手机客户端上使用的动态化创建界面的方案,通过数据描述页面结构以及包含的组件类型,然后动态地创建出页面结构、实例化出组件的对象,从而生成一张完整的页面。

序列化:在本申请中,指将文本类型的数据通过一定的协议转换成二进制数据的过程。

在电商类产品中,经常会碰到临时性的需求调整,针对这些调整所要进行的很多改动都是界面视图上的改动。但是,传统的界面开发技术只能在发布新版本的时候去更新界面视图,无法针对已经发布的应用进行动态更改。为了解决动态化、性能的问题,业界开始采用基于虚拟化组件的开发方式进行界面视图的开发。

例如,对于虚拟化组件,直接使用canvas绘制视图。但是直接绘制必须根据特定场景定制的业务逻辑来实现,不具备通用性,使得每个场景都需要写代码开发尺寸计算、布局位置计算等逻辑。而且,按此方式,对于混合组件的开发,也只能直接地根据业务场景将虚拟化组件和原生组件放到一个原生容器视图里,堆叠到一起。这样不仅增加了布局层次,同时也不具备通用性。

又如,有国外厂商开源过一套框架litho(https://fblitho.com/),定义了mount和layout。其中,mount可以用来规范组件的创建,包括常规的原生组件以及可以直接在canvas上绘制的drawable对象;layout可以用来做布局。但是,litho存在如下不足:一是该方案只能在android平台上实现,无法在ios平台上使用;二是该方案依赖的大量周边设施依赖于该厂商自有的架构体系,从编译到运行各个环节均需要依赖该厂商的基础设施,无法在其他的产品中直接使用;三是该方案在创建业务组件时需要写代码,无法动态更新。

另一方面,基于业界已有的移动界面开发工具进行的界面视图开发,虽然能够实现一定的动态修改,但其仍然具有一定的局限性。

例如,luaview。luaview是使用lua脚本语言实现android、ios动态化的一套方案,它包含lua虚拟机、脚本管理模块、安全控制模块、ui核心库、非ui核心库等主要模块。其中,虚拟机模块用来解释执行lua脚本;脚本管理模块用来解压缩、加密、验证脚本等功能;安全控制模块用来对脚本的完整性和安全性进行校验;ui核心库用来构建各平台上的基础ui控件,比如button、label、image等等;非ui核心库用来调用各平台上的基础设施,比如http请求、json数据解析、访问音频audio等等。业务方在这些基础设施之上写lua脚本,通过sdk运行脚本来生成界面。脚本可以动态更新,开发的界面也可以动态更新。luaview可以做整个界面,也可以只做一个组件。但是,luaview是在已有的平台框架基础之上搭建了新的解释执行层,必然会有额外的性能损耗;而且,lua语言相对冷门,采用luaview进行界面视图的开发时,开发者需要额外学习lua语言才能入门。

又如,weex。weex除了可以部署在android和ios上运行外,还能在浏览器上运行。weex主要包含了转换器模块、js运行环境、渲染模块三大组成部分。开发者可以像撰写web页面一样撰写一个app的页面,然后通过转换器模块编译成一段javascript代码,形成weex的一个jsbundle。在云端,开发者可以把生成的jsbundle部署上去,然后通过网络请求或预下发的方式传递到用户的移动应用客户端。在移动应用客户端里,weexsdk会准备好一个javascript引擎,并且在用户打开一个weex页面时执行相应的jsbundle,并在执行过程中产生各种命令发送到native端进行的界面渲染或数据存储、网络通信、调用设备功能、用户交互响应等移动应用的场景实践。同时,如果用户没有安装移动应用,他仍然可以在浏览器里打开一个相同的web页面,这个页面是使用相同的页面源代码,通过浏览器里的javascript引擎运行起来的。但是,weex并不是为了解决纯native开发的体验问题,也不是以自身为中心开发的整套移动应用。因此,在很多重要的页面里,还无法用weex来代替native开发。如果native页面某个局部使用weex技术来开发组件,也无法最大化地发挥weex的优势。因为每一个组件都会需要一个weex实例来运行,对页面滑动和加载速度有很大影响。

再如,reactnative。reactnative也能部署到android、ios和浏览器上。它也包含了一个js运行环境、js-native通信模块、dom管理模块,以及一系列基础设施如图片加载模块、网络请求模块、工具模块等等。在使用时,开发者也是通过编写js代码来开发页面组件,reactnative框架通过执行js代码动态创建出native组件,并通过js-native通信模块实现两者js层和native层的方法调用、消息传递。但是,与weex一样,reactnative也无法代替native开发页面。如果采用reactnative来开发组件的话,也会影响整体页面的性能和体验。

因此,基于上述分析,提出了本申请实施例的核心构思之一在于,通过虚拟化组件,本申请重新定义了一种虚拟文本组件或者线条,或者虚拟化容器组件,简化了大量不必要的逻辑。开发者能够按照各自的需求定制功能,并能够减少大量不必要的开销。同时,本申请将虚拟化组件都绘制到宿主容器的canvas上,使得最终产生的界面视图只有一个原生组件,嵌套层次也只有一层结构,从而在将组建提交给系统显示模块绘制的时候能够更加的快速高效。此外,基于虚拟组件系统,本申请还可以搭建动态化调整界面视图的模块,使得界面开发时的整体性能优于采用weex或在html5标准开发的组件性能。

本申请实施例提供了一种组件体系。为了便于理解,首先对本申请实施例的组件体系作一说明。

参照图1,示出了本申请的一种组件体系的结构框图,其中,iview定义协议接口,包括三个过程:

commeasure/oncommeasure:尺寸计算;

comlayout/oncomlayout:尺寸布局;以及,

comdraw/oncomdraw:组件视图绘制。

当客户端在进行业务组件的绘制时,会分别调用上述几个过程。

在图1中,viewbase定义组件的基础属性;虚拟组件都继承自virtualviewbase,虚拟容器组件都继承自virtuallayoutbase,原生组件则可以按照本申请实施例提供的协议进行封装,这个封装需要继承自nativeviewbase。

这样,虚拟组件和原生组件都有共同的对外接口。在客户端绘制时,不论虚拟化组件还是原生组件都可以采用通用的方式进行调用,为混合方式搭建业务组件提供了可能性。

在图1中,各个叶子节点可以是一些内置定义的基础组件,如virtualviewbase组件、virtualtext组件、virtualimage组件、virtualline组件等等。当然,只需要按照本方案的协议扩展,即可以衍生出其他类型的组件。

在本申请实施例中,内置的基础组件可以包括如下的一种或多种:

虚拟文本组件、原生文本组件、虚拟图片组件、原生图片组件、虚拟线条组件、原生线条组件、虚拟进度条组件、虚拟图形组件、原生翻页布局容器组件、原生滚动布局容器组件、虚拟帧布局容器组件、虚拟比例布局容器组件、虚拟网格布局容器组件、原生网格布局容器组件、虚拟线性布局容器组件、原生线性布局容器组件。

进一步地,参照图2,示出了本申请的一种组件体系的整体架构示意图。在整体架构中,主要包括三个层次,底层是基于虚拟组件技术的ui库和简单的表达式引擎。组件体系已经介绍过,资源加载器就是从编译后的模板数据里按照协议加载数据并还原构造出上述的组件实例。

表达式引擎主要负责数据的获取与简单的逻辑运算,可以有两种类型的表达式使用,一种是简单的数据绑定表达式,另一种则是简单的逻辑表达式。

其中,数据绑定表达式的场景是这样的:在开发业务组件的时候,ui元素的基础属性或者样式往往不能在模板里直接写死,而是需要从数据里获取,所以引入了用户数据绑定的表达式,包括对象属性的访问(即访问数据属性的表达式)和三元操作符。

访问数据属性的表达式基于标准javabean规范,通过表达式访问对象的属性,语法上以${开头,以}结束。对于pojo以及map,可以通过.操作符(点操作符)进行访问,对于array或者list,则可以通过[]操作符进行访问。

需要说明的是,本申请在设计规范上是引用了javabean访问的规范,在普通java代码里实现这套规范,区分访问对象的类型就是通过类型判断。在本申请实施例中,由于访问的是json数据,其实json数据也有json对象,json数组之分,也只要判断对象的类型即可。因此,在写表达式的时候,用户是首先知道他处理的数据对象的结构信息的,所以按照这个结构信息来写表达式就能获取到数据。

如下所示,是本申请的一种访问数据属性的表达式的示例:

classperson{stringname;publicperson(){name="jack";}}list<object>list=newarraylist<object>();list.add("person",newperson());list.add("url2");map<string,object>data=newhashmap<string,object>();data.put("list",list);data.put("logo","https://gw.alicdn.com/tps/tb1wwikkv.png");

${logo}:https://gw.alicdn.com/tps/tb1wwikkv.png

${list[0].name}:jack

{"id":null,"name":"doe","first-name":"john","age":25,"hobbies":["reading","cinema",{"sports":["volley-ball","badminton"]}],"address":{}}

${first-name}:john

${hobbies[0]}:reading

${hobbies[2].sports[0]:volley-ball

而三元操作符则是用来给那些需要根据数据中某个字段来设置值的属性。例如,当存在titlecolor字段的时候,可以设置文本的颜色为titlecolor指定的值,否则使用默认的黑色。在语法上,三元操作符以@{开头,以}结束,中间部分为表达式的具体内容。

条件表达式?结果表达式[1]:结果表达式[2]

当条件表达式成立的时候,使用结果表达式[1],否则使用结果表达式[2]。

其中,条件表达式可以支持布尔类型、字符串类型、jsonobject、jsonarray等。

作为本申请的一种示例,以下场景均为false:

布尔类型值为false;

字符串为null或者""或者"null";

字符串"false"或者"false";

jsonobject为空或jsonobject.null;

jsonarray长度为0;

字段不存在。

如下所示,是本申请的一种三元操作符表达式的示例:

@{${logourl}?visible:invisible}

@{${titlecolor}?${titlecolor}:black}

与数据表达式不同,逻辑表达式具备更加强大的能力。能够执行算术运算、逻辑运算、条件语句、函数调用等等。逻辑表达式可以访问基础组件的属性并更新,实现一些联动效果。

在语法上,逻辑表达式以${开头,以}结尾,一般写在基础组件的事件触发属性里。例如,点击事件onclick,翻页事件onpageflip等等,当事件触发的时候执行一段逻辑。

在本申请实施例中,包括如下几个访问基础组件元素的关键词:

this:当前触发逻辑表达式执行的基础组件对象;

parent:当前触发逻辑表达式执行的基础组件对象的父节点;

ancestor:当前触发逻辑表达式执行的基础组件对象的所有上级节点,包括父节点;

siblings:当前触发逻辑表达式执行的基础组件对象的兄弟节点;

data:当前触发逻辑表达式执行的基础组件所绑定的数据对象。

作为本申请的一种示例,在翻页组件发生滚动的事件里写上onpageflip="${this.parent.title.textcolor=data.color[this.curpos]}",表示在每一次翻页的时候,找到父节点下名为title的一个组件,将它的文字颜色更新一下,颜色值取自数据里的color数组,索引是当前翻页停留的索引位置。

需要说明的是,在模板里编写的表达式资源,会通过${}关键字识别出来,与普通的字符串做不一样的处理。在具体实现中,可以将它们通过.=[]等关键字解析分割,保存到独立的表达式资源区里。当应用程序解析整个组件过程中,碰到表达式的时候,可以从表达式资源池里获取信息,按照表达式描述逐步访问对应的资源,逐步找到整个表达式期望的最后一级节点指向的资源。如果中间存在错误或者发现找不到,那就可以认为该资源不存在。

基于上述的底层虚拟组件技术和表达式能力,可以构建起对组件的管理与能力扩展,其中:

模板加载器负责加载编译后的模板数据。例如,从文件加载、从二进制数组加载。它主要将二进制模板数据加载到内存里,提取出其中的资源。例如字符串资源、表达式资源,并不做组件的构造。

容器管理负责对虚拟组件的宿主容器进行构建和回收复用的管理。当原有的组件滑出屏幕后,可以回收到统一的资源池子里,以便后续复用。如果池子是空的,则在需要的时候构造新的容器。

组件管理负责对组件进行构建和回收复用管理。当原有的组件滑出屏幕后,除了宿主容器可以回收复用,内部的虚拟组件对象也可以回收到统一的资源池子里。如果组件的池子是空的,则在需要的时候构造新的组件。

事件管理,虚拟组件技术主要负责界面的动态化创建,但对业务逻辑的处理主要还是靠原生的代码实现。因此,在本申请实施例中,需要处理组件的一些常用交互事件。例如,组件的点击、长按、触摸、曝光事件等。事件管理模块负责将外部的各个类型的事件处理模块注册进来,当组件发生特定的事件时,找到对应类型的处理模块来调用处理。

native对象管理模块和服务管理模块的功能类似,都是为了将外部功能模块注册到本实施例的组件框架里。区别在于,native对象模块是将功能模块注册到某个单独的组件里,可以在组件模板里配置该相应的功能。

例如,对于模板里的如下内容:

<ximage

flag="flag_clickable"

class="ximagebean"

src="${bgimgurl}"

layoutwidth="match_parent"

layoutheight="match_parent"

visibility="@{${bgimgurl}?visible:gone}"/>

class属性指定了一个native对象类来扩展当前ximage基础组件的能力,在组件创建的时候会读取到class属性里的值,并在最后创建出实例来。ximagebean需要实现一个接口:

//初始化

voidinit(contextcontext,viewbaseview);

//销毁释放资源

voiduninit();

//处理消息

voiddoevent(inttype,intparam,objectobjparam);

用户在自定义ximage组件的特定时机调用一下doevent方法。如果ximagebean的实现是给ximage父容器的边框增加一层颜色,那么当ximage运行过程中调用了doevent方法之后,就实现了它父容器边框颜色的变化。

而通过服务管理模块注册的外部功能模块则是框架实例运行期间全局使用的,不在模板里进行配置,需要代码级别调用。用户在初始化框架的时候就需要注册一个外部功能模块对象到服务管理模块内。用户要使用外部功能模块,就需要编写一个自定义基础组件,主动去调用获取功能模块,这样可以解除外部功能模块和本实施例中组件框架的耦合。

有了基础的虚拟组件技术,以及对它的能力扩展,就可以用来开发界面视图。但为了动态化更新界面,还需要进一步封装动态更新的能力。所以在上述两个层面之上,还需要构建组件模板的管理、更新能力。

模板管理模块负责对本地存在的模板进行存储、更新、完整性校验、版本校验等。

模板更新模块则负责对比本地的模板版本和远程模板管理平台上发布的版本,然后下载更新的模板数据,并更新本地的数据。

数据加载模块则是负责加载界面展示所需要的数据,在组件创建好之后传递给组件,展示真实的业务需求。

在运行虚拟组件的环境之外,本申请实施例也提供了一个开发工具,能够编写组件的xml模板,并包含了模板编译工具,将xml编译成二进制数据。本实施例还提供了一个模板管理后台,用于在线发布新的模板数据。

基于上述组件体系,本申请实施例引入了在xml里使用基础组件来开发业务组件,然后通过编译工具将xml文件编译成二进制数据,接着在客户端上解析二进制数据来渲染组件,并支持将业务数据绑定到组件上。在绑定数据的过程中,本申请实施例支持简单的表达式逻辑来动态地获取绑定数据,更新视图。同时整个框架也支持使用方注册外部功能模块到内部,提升整体的能力。

参照图3,示出了本申请的一种客户端视图的绘制方法实施例一的步骤流程图,具体可以包括如下步骤:

步骤301,获取业务组件模板,所述业务组件模板包含原始模板数据;

在本申请实施例中,可以通过在可扩展标记语言xml里使用基础组件的方式来开发业务组件。因此,业务组件模板可以是由组件开发人员通过工具预先编写的xml模板。

如图4所示,是本申请的一种客户端视图的绘制方法实施例一的运行时序图。在组件开发人员编写好xml模板后,可以获取预先编写的上述xml模板。

在本申请实施例中,业务组件模板包含有原始模板数据。如下所示,是本申请的一种xml模板的原始模板数据的示例:

当然,根据实际需要,本领域技术人员也可以采用其他格式语言编写业务组件模板,本实施例对此不作限定。

步骤302,将所述原始模板数据编译为目标数据;

在本申请实施例中,将原始模板数据编译为目标数据即为将原始模板数据序列化为目标数据,目标数据可以是二进制数据。

以业务组件模板为xml模板为例,编译过程即为图4中所示的将xml序列化为二进制数据。

如图5所示,是本申请的一种编译原始模板数据的流程图。首先,可以创建文件对象。在编译工具开始编译业务组件模板时,可以创建一个输出文件的对象,指向特定路径。后续的在编译过程中的数据都可以写入这个文件中。

需要说明的是,创建的文件对象可以包括多个区域。例如,组件区、字符串区、表达式区,以及,数据区。

然后,可以在上述文件对象中写入文件格式标记信息、版本号数据,以及,各个区域的占位空间信息。

在具体实现中,按照文件格式,可以在开头5个字节固定为文件格式标记信息。例如,alivv。当然,上述alivv仅为一种示例,本领域技术人员可以根据实际需要,选择其他字符或字符串作为文件格式标记信息,本实施例对此不作限定。

在写入文件格式标记信息后,紧接着的6个字节可以写入三位版本号。其中,主版本号可以固定为1,次版本号可以固定为0,修订版本号则可以在每次编译时由组件开发人员通过参数传入,从1开始。

在本申请实施例中,按照文件格式,接下来的32个字节可以分别为组件区、字符串区、表达式区,以及,数据区的起始位置和长度。因此,可以先占位,初始化为0。

当然,还可以写入当前文件页面编码,以及它的依赖等信息。这些信息也可以是由组件开发人员在编译时传入。默认的页面编码为1,如果不存在依赖的页面,则着一部分信息不占空间。

通常,一个业务组件对应着一个模板。因此,在编译时,可以首先读取获取到的业务组件模板中的原始模板数据,并创建业务组件模板解析器。

在具体实现中,若业务组件模板为xml模板,则创建的业务组件模板解析器可以为xml解析器。

在本申请实施例中,在读取原始模板数据后,可以首先遍历原始模板数据的各个节点。

在具体实现中,可以由xml解析器按照xml格式遍历上述xml模板的各个节点,获得各个节点均相应的名称和多个属性。

在本申请实施例中,可以根据各个节点的名称,创建对应的基础组件编译器。

在具体实现中,可以在开始遍历时,获取一个节点名称,并记录节点开始标记,然后根据节点名称字符串,创建对应的基础组件编译器对象。在编译工具里,每一个基础组件都注册了对应的编译器类型。用户开发自定义基础组件,也需要提供自定义编译器注册到编译工具里。基础组件和对应的编译器类可以通过组件类型关联起来。

然后,可以获取各个节点的全部属性,并采用上述创建的基础组件编译器对获取的全部属性进行处理,获得多个属性值。

例如,可以每获取到一个属性,就调用编译器处理该属性。编译器知道每个属性应该如何处理,因为这是定义属性、开发编译器类的时候便确定的。每一种属性都会被序列化成以下4种类型:int整型属性值、float浮点型属性值、string字符串型属性值,和/或,表达式类型属性值。其中前两者可以直接作为序列化后的值写到返回结果里,后两者可以先转换为对应的哈希码值hashcode作为序列化后的值写到返回结果里。

当遍历完当前节点的全部属性后,可以将多个属性值写入预先创建的文件对象中。

在具体实现中,当当前节点处理完毕后,可以写入一节点结束标记,并检查是否遍历完所有节点。如果还存在其他节点,则对未处理的节点进行处理。若全部节点均处理完毕,则可以按照整型属性值、浮点型属性值、字符串型属性值,和/或,表达式类型属性值对多个属性值进行分类,生成key-value字节数组,其格式可以为4字节key索引+4字节value索引。然后,将上述key-value字节数组写入文件对象的组件区。

而对于字符串型属性值和表达式类型属性,可以首先分别将字符串型属性值和表达式类型属性值转换为对应的哈希码值hashcode,然后将将字符串型属性值和表达式类型属性值对应的哈希码值分别写入文件对象的字符串区和表达式区。

这样,预先创建的文件对象的组件区、字符串区和表达式区的起始位置都知道了,就能够更新事先占位的各个区域了。

需要说明的是,若存在扩展数据,则可以将扩展数据写入数据区。

在全部写完后,可以将所有数据输出到文件,文件后缀可以为.out。

需要说明的是,一个业务组件对应着一份xml模板,一个模板单独编译成二进制数据,它会包含除内置字符串资源以外所有它依赖的所有字符串资源、表达式资源。

在编译的时候,所有模板里涉及到的资源包括颜色值、各种枚举、基础组件的类型等都会被序列化映射成整数,而不能序列化成整数的资源比如字符串,就给它分配一个索引id指向它,并将它们单独存储到一块区域里。序列化的规则可以如下:

颜色:转换成4字节整型颜色值,格式为aarrggbb;

枚举:按照预定义的整数转换,比如gravity的类型,orientation的类型等;

基础组件的类型:按照预定义的整数转换,内置基础组件的类型可以从1开始分配,自定义组件可以从1000开始分配,后续可能语义化成字符串类型定义,对用户隐藏上述细节;

字符串:以hashcode值作为它的序列化后整数,并在字符串资源区建立以hashcode为索引的列表,在解析的时候从中获取原始的字符串值;

逻辑表达式:与字符串的处理类似。

由于原始模板数据是一段xml格式的文件,它保存成文件的时候,就是以纯文本的形式存在,会包含很多冗余信息。例如,空格、换行、还有重复出现的字符串等等,文件体积比较大。以xml解析器去解析的时候,也会需要大量字符串操作,效率和性能不能达到最优。而当将其编译成二进制格式后,则能够避免这些问题。例如,文件重复出现的字符串只保留一份,通过字符串索引去引用它,所有的组件类型也都会被转换成一个数字索引,在客户端内通过数字索引反过来找到对应的类实例化。这样,文件格式会非常紧凑,体积更小。

如图6所示,是本申请的一种序列化后的组件模板文件格式的示意图。它的具体格式说明如下(按照图6中从左往右、从上往下的顺序分别说明每个段的作用):

开始5个字节固定为alivv(文件格式标记信息);相当于文件格式的一个标记;

版本号分三个,分别为主版本号、次版本号和修订版本号,均为2个字节;在无重大重构更新时,前两位一般不变,第三位用于组件的业务级别变更升级;

组件区的起始位置和长度,均为4个字节;表示这份文件里组件区数据从第几个字节开始,它总共有多少个字节,这样解析这份数据的时候能直接将文件指针定位到特定位置来读取数据;

字符串区的起始位置和长度,均为4个字节;表示这份文件里字符串数据从第几个字节开始,它总共有多少个字节;

表达式区的起始位置和长度,均为4个字节;表示这份文件里字符串数据从第几个字节开始,它总共有多少个字节;

数据区的起始位置和长度,均为4个字节;表示这份文件里附加数据从第几个字节开始,它总共有多少个字节;

当前文件所属页编码,2个字节,唯一标识一个页;

当前文件依赖页的个数为2个字节,后面为依赖页的id,依赖页个数大于0表示该页用到了其他页的资源或者代码,在该页加载之前需要确保依赖页必须已经加载;

组件区开始,前4个字节表示文件里业务组件个数,目前一个xml模板编译成一个二进制文件,故其值固定为1。每个业务组件前2个字节表示业务组件名称字符串的长度,后面为指定长度的字符串字节数据;紧接着是2个字节的编译后组件二进制流长度,后面为二进制代码;

字符串区开始,前4个字节表示字符串个数,在本申请的框架里,可以内置一些系统级别的字符串资源,例如,属性名。这些字符串不用序列化到二进制文件里,而模板文件里出现的非系统字符串才会作为资源序列化到二进制文件。每个字符串资源前4个字节字符串索引id即它的hashcode,后面2个字节为字符串的长度,再后面为对应的字符串;

逻辑表达式代码表,前4个字节表示逻辑表达式资源个数,每个表达式资源前4个字节表示表达式的索引,它是表达式原始字符串的hashcode,后面2个字节表示表达式的长度,后面为对应的表达式内容,它是表达式按照关键字切割后的字符串结构;

扩展数据段是保留为第三方扩展使用。

在本申请实施例中,将原始模板数据编译为目标数据后,还可以将上述目标数据加载至客户端。

在具体实现中,将编译后的目标数据加载至客户端可以通过两种方式实现。参照图4,一种方式可以是直接将目标数据打包到客户端内,写代码加载。

例如,可以根据目标数据,生成目标数据包,并通过预设代码,将上述目标数据包加载至客户端。

另一种方式可以将目标数据发布到模板管理后台,由客户端在线更新。

例如,可以发送编译好的目标数据至预置的模板管理平台,然后由客户端通过上述模板管理平台在线更新目标数据。

步骤303,对所述目标数据进行解析,以获得相应的目标模板数据;

无论采用哪种方式加载目标数据,客户端接下来的工作便是解析目标数据。例如,校验版本号,合法性,读取头信息等等。

由图4所示的时序图可以看出,编译阶段是独立于客户端运行时的一个过程,客户端渲染组件,是从解析编译好的二进制数据开始的。解析其实就是编译过程的逆过程,但解析流程只负责把原始数据提取出来,组织好格式,并没有直接构建出组件对象。

如图7所示,是本申请的一种解析目标数据的流程图。以目标数据为二进制数据为例。首先,可以获取一个编译好的二进制文件对象,并将该二进制文件对象读取到一个字节数组中。然后,校验该文件对象中的数据是否为非法数据。

在校验时,可以首先校验文件对象中的数据的字节长度和版本号数据是否满足预设要求。若文件对象中的数据的字节长度和版本号数据满足预设要求,则可以确定该文件对象中的数据不为非法数据。

在具体实现中,可以首先校验二进制文件的字节长度是否大于27字节,再校验开头的5个字符是否为alivv。由于后续可能对文件格式有升级,如果主版本号和次版本不同,可能导致数据不兼容。因此,可以再检查后面两位主版本号和次版本号是否匹配。如果不满足上述条件,认为是非法数据,可以作放弃处理;否则可以开始下一步操作,分别确定该文件对象的各个区域中的数据的起始位置和字节长度。

在具体实现中,确定文件对象的各个区域中的数据的起始位置和字节长度可以包括:确定组件区中的数据在文件对象中的起始位置,和该组件区中的数据的字节长度;和/或,

确定字符串区中的数据在文件对象中的起始位置,和该字符串区中的数据的字节长度;和/或,

确定表达式区中的数据在文件对象中的起始位置,和该表达式区中的数据的字节长度;和/或,

确定扩展区中的数据在文件对象中的起始位置,和该扩展区中的数据的字节长度。

例如,可以读取组件区数据在文件里的起始位置,以及总共多少个字节是组件数据;读取字符串区数据在文件里的起始位置,以及总共多少个字节是字符串数据;读取表达式区数据在文件里的起始位置,以及总共多少个字节是表达式数据;读取扩展区数据在文件里的起始位置,以及总共多少个字节是扩展数据。

当获得上述信息后,便可以依据上述起始位置和字节长度,分别获取各个区域中的数据。

在本申请实施例中,可以依据组件区中的数据在该文件对象中的起始位置,和该组件区中的数据的字节长度,获取组件名字符串;并确定组件区中的数据中,除组件名字符串外,其他的组件数据的起始位置;然后,可以根据组件名字符串,读取出相应的组件名;进而建立组件名与组件数据的起始位置之间的映射关系。

在具体实现中可以先将文件指针定位到组件区,按照文件格式,先解析出组件名字符串,记录下读取完组件名后真实组件数据开始的位置;建立以组件名为key,到组件数据起始位置的映射。当后续用户在构造业务组件的时候只要传入组件名,就可以直接找到对应的组件数据进一步构造组件。

对于字符串区的数据,可以依据字符串区中的数据在文件对象中的起始位置,和该字符串区中的数据的字节长度,获取字符串个数;并根据字符串个数,循环读取每个字符串资源,获得字符串索引;然后,可以根据字符串资源,生成字符串;进而分别建立字符串索引与字符串之间的映射关系。

在本申请实施例中,在建立字符串索引与字符串之间的映射关系后,还可以将上述字符串索引与字符串之间的映射关系存储至预置的字符串资源池中。

在具体实现中,在开始解析处理字符串数据时,可以将文件指针移到字符串区开始的位置,按照文件格式,先读取4字节,获取字符串个数;然后,根据字符串个数,循环读取每个字符串资源。每次先读取4字节索引,再生成字符串;每次读取完一个字符串,便可以建立索引到字符串的映射,并保持到字符串资源池里。

类似地,对于表达式区的数据,可以依据表达式区中的数据在文件对象中的起始位置,和该表达式区中的数据的字节长度,获取表达式个数;并根据表达式个数,循环读取每个表达式资源,获得表达式索引;然后,可以根据表达式资源,生成表达式对象;进而分别建立表达式索引与表达式对象之间的映射关系。

在本申请实施例中,在建立表达式索引与表达式对象之间的映射关系后,还可以将上述表达式索引与表达式对象之间的映射关系存储至预置的表达式资源池中。

在具体实现中,在开始处理表达式数据时,可以将文件指针移到表达式区开始的位置,按照文件格式,先读取4字节,获取表达式个数;然后,根据表达式个数,循环读取每个表达式资源。每次先读取4字节索引,再将表达式内容封装成表达式类型结构;每次读取完一个表达式资源,便可以建立索引到表达式的映射,并保持到表达式资源池里。

当处理完一个二进制文件后,解析的过程便结束了。然后可以执行步骤304,创建业务组件。

步骤304,依据所述目标模板数据,创建业务组件;

在本申请实施例中,真正创建出一个组件,需要在创建流程中完成。当用户传入一个组件名称,框架内部就会根据该名称去之前解析好的数据里找到与此名称匹配的模板数据,然后构造出组件来。整个创建组件的过程启示就是编译过程的逆过程。

如图8所示,是本申请的一种创建业务组件的流程图。首先,可以获取待创建的业务组件的组件名。待创建的业务组件的组件名是在解析阶段解析出来的名称,否则找不到对应的组件数据,无法创建组件。该组件名可以由用户传入。因此,客户端可以接收用户传入的待创建的业务组件的组件名,然后根据待创建的业务组件的组件名,获取相应的目标模板数据,并采用目标模板数据,构造业务组件。

在本申请实施例中,可以根据待创建的业务组件的组件名,和组件名与组件数据的起始位置之间的映射关系,查找相应的组件数据。

在具体实现中,找到原始组件数据,并通过之前记录的组件数据起始位置直接定位到二进制文件组件数据开始的地方,准备开始创建组件。

在创建业务组件时,首先可以依次读取组件数据中的各个字节,当读取到的当前字节的标记信息为节点开始标记时,获取当前字节的节点类型;并根据当前字节的节点类型,创建基础组件实例;然后,依次读取组件数据中的多个属性值;例如,依次读取组件数据中的整型属性值、浮点型属性值、字符串型属性值,和/或,表达式类型属性值;进而可以根据key-value字节数组,将多个属性值对应的信息发送至基础组件实例中对应的属性。

在本申请实施例中,在将多个属性值对应的信息发送至基础组件实例中对应的属性时,可以首先确定当前属性值的数量。若当前属性值为整型属性值或浮点型属性值循环,则可以从key-value字节数组中读取上述整型属性值或浮点型属性值对应的信息;并将整型属性值或浮点型属性值对应的信息发送至基础组件实例中对应的属性。

若当前属性值为字符串型属性值或表达式类型属性值,则可以根据字符串索引或表达式索引,从字符串资源池或表达式资源池中获取对应的信息,并发送至基础组件实例中对应的属性。

在具体实现中,在进入循环时,可以先读取一字节,判断该字节是节点开始标记还是节点结束标记。

若是节点开始标记,表示刚开始处理一个新的基础组件的信息。此时,可以读取该字节的节点类型。

然后,可以根据节点类型创建基础组件实例。这个类型与组件类型的映射关系也是框架已经提前注册好或者用户注册过的。序列化后的字节类型值和编译器类、基础组件实现类都是一一对应的。如果已存在创建好的基础组件节点,说明对当前新节点有父容器,可以将它做压栈暂存。

在创建基础组件实例后,可以为该基础组件实例设置布局参数对象,与布局相关的参数信息最终均会传给这个布局参数对象。

然后,可以按照int整型、float浮点型、string字符串、表达式类型这四种属性的顺序开始读取四种属性。每读取一种类型的属性,都可以进入一个小循环。即:先读取该类型属性的值的数量;再循环读取其中的key、value的值,按照文件格式,key、value的值都是四字节;然后根据属性的key值,传递相应的value值给基础组件实例里对应的属性。如果碰到字符串型或者表达式类型的属性,则可以通过索引去解析过程里构造的字符串池或者表达式池里读取出具体对象传给基础组件实例。

当处理完所有属性后,可以继续读取下一字节,判断该字节是节点开始标记还是节点结束标记。

在本申请实施例中,当读取到的当前字节的标记信息为节点结束标记时,可以首先确认是否存在上次创建的基础组件节点。

当存在上次创建的基础组件节点时,可以继续确定上次创建的基础组件节点是否为容器类型;若是,则可以将当前节点添加至上次创建的基础组件节点中;若否,则结束当前的业务组件的创建;

当不存在上次创建的基础组件节点时,也可以结束当前的业务组件的创建。

在具体实现中,当读取到的当前字节的标记信息为节点结束标记时,说明当前节点是上次创建节点的子阶段,需要检查上次创建的节点是否是容器类型。

由于非容器基础组件节点不能嵌套其他基础组件节点,因此,当上次创建的节点不是容器类型时,整个组件创建失败。

当上次创建的节点是容器类型时,可以将当前节点添加到父容器节点里,上次暂存的父容器节点出栈。

至此,便完成了业务组件的创建。

步骤305,获取匹配所述业务组件的业务数据;

在本申请实施例中,业务数据可以是json格式的数据,该数据可以是由组件开发人员传入的。

通常,基础组件的属性往往不能静态写死,需要动态地根据数据来设置。因此模板里支持写一种简单的数据绑定表达式,等数据到达的时候,通过表达式里的定义可以访问数据里真实值以便设置给组件的属性。

在创建组件的过程中,当解析属性碰到表达式的时候,就会将该属性的key、表达式值、所属的基础组件等关系存储起来,等真实的数据到达之后再将真实的数据绑定到基础组件的属性上。

通过表达式解析、访问得到的属性值,均会缓存起来。当原始数据引用不变的时候,每次访问都会获取到缓存值。如果数据引用不变,但内容变了,希望绑定更新后的数据,那可以在数据里增加字段_flag_invalidate_:true,表示需要更新缓存内容。

步骤306,将所述业务数据绑定至所述业务组件中。

在本申请实施例中,在进行业务数据的绑定时,可以首先确定业务组件中待绑定数据的基础组件,然后确定待绑定数据的基础组件中待绑定数据的当前属性,进而判断从缓存中是否可获取到当前属性的目标值,上述缓存可以通过对所述当前属性的解析获得。若是,则可以根据当前属性的类型,将上述目标值赋值给基础组件实例对应的属性;若否,则需要从业务数据中获取当前属性的目标值并缓存,然后才能将上述目标值赋值给基础组件实例对应的属性。

需要说明的是,在确定出待绑定的属性后,还可以确定当前属性中是否包括待清理缓存标记,若是,则可以清理当前属性中的数据值缓存;若否,则可以返回执行判断从缓存中是否可获取到当前属性的目标值的步骤。

如图9所示,是本申请的一种绑定业务数据的流程图。在具体实现中,针对一个业务组件,可以首先获取到它所宝行的基础组件列表。在开始循环时,依次检查每个基础组件是否需要绑定数据。如果没有需要绑定数据的基础组件,则结束当前流程;否则开始业务数据的绑定流程。

当存在需要绑定数据的基础组件时,可以获取该基础组件里需要绑定数据的属性列表,该列表在创建业务组件的过程中会随之创建并保存。

在开始循环处理每一个属性时,可以首先检查原始数据里是否有清理缓存的标记。若是,则可以清理掉之前可能已经通过表达式获取过的数据值缓存;否则,可以从缓存里获取之前可能通过表达式解析获取过真实内容。

当获得缓存值后,便可以根据属性类型,将真实值设置给基础组件实例对应的属性,然后处理下一个属性。

如果未能获得缓存值,则可以根据表达式的语法定义和访问数据的逻辑,从原始数据里获取真实内容,并缓存获取到的值,以便二次使用。

当完成业务数据的绑定后,也就完成了组件视图的绘制。参照图2所示的时序图,在完成业务数据绑定后,便可以在客户端中展现上述业务组件。

在本申请实施例中,通过标准化的模型定义以及约定的渲染流程,每个基础组件都可以单独开发,并且在开发完毕之后可以自由组合,像搭积木一样搭建出复杂的视图,具备极强的通用性。业务方在使用的时候不用再采用写原生的自定义view或在canvas上绘制视图的方式来创建业务组件。即使在遇到内置的基础组件不满足相应的业务场景的时候,也只需要按照协议开发新的基础组件即可,并且开发好的基础组件也能够提供给其他业务方使用。

第二,本申请实施例提供的方案不仅能在android平台上实现,在ios平台上也能够实现,并且能够保证在android平台和ios平台上两端一致,为后续更丰富的组件体系开发打下了基础。而且,本方案也不需要依赖任何特殊的设施,在任何应用程序中均可以直接使用。

第三,基于本申请实施例提供的方案,可以开发一整套完备的组件体系。在内置组件的使用、业务组件的搭建时,均无需通过写代码的方式去实现,而是可以直接在xml模板里使用它们,然后使用特定的工具将模板编译、最后使用的时候反过来解析出组件实例并最终渲染出来即可。在整个过程中,业务方使用时可以脱离代码的限制,不用写代码也能开发出一个新的业务组件,提高了业务组件开发的效率。

第四,在本申请实施例中,可以采用大家熟悉的xml编写模板,对于客户端开发来说没有门槛,只需要熟悉内置组件的使用即可。在开发时,不需要学习lua语言,也不需要学习js。虽然js学起来很容易,但采用本申请实施例提供的方案开发业务组件,连js都可以不用熟悉。

第五,本申请实施例提供的方案都是按照轻量级、小而快的原则开发业务组件,没有引入重量级的框架、解析器等,能够最接近原生的代码开发,具有优异的组件性能。

参照图10,示出了本申请的一种客户端视图的绘制方法实施例二的步骤流程图,具体可以包括如下步骤:

步骤1001,当接收到绘制客户端视图的指令时,确定各个业务组件的尺寸大小;

在本申请实施例中,各个业务组件的尺寸大小可以通过各个业务组件的属性获得。

在本申请实施例的组件体系中,viewbase是整个组件模型的定义。它定义了一个组件的基本属性,包括id、宽高、内边距、外边距等等。

如表一所示,是本申请实施例的一种业务组件的属性的示例。

表一:业务组件的属性

这些属性主要用来控制组件的宽、高尺寸计算,以及布局的时候排版定位,还有一些简单的样式。例如,layoutwidth和layoutheight分别定义了组件的宽、高,它可以是具体的数值,比如100,也可以是一个定量的描述。match_parent和wrap_content从android系统的原生布局系统里引入,前者表示当前组件的宽度要撑满父容器给定的宽度,后者表示当前组件的宽度要根据自身内容的大小来决定,此行为与android系统的表现一致。

在本申请实施例中,当接收到绘制客户端视图的指令时,可以分别确定宿主容器组件、虚拟容器组件,和/或,基础组件的尺寸约束条件,然后根据宿主容器组件和虚拟容器组件的尺寸约束条件,确定宿主容器组件和虚拟容器组件的尺寸大小,并根据虚拟容器组件的尺寸大小和基础组件的尺寸约束条件,确定各个基础组件的尺寸大小。

在绘制客户端界面视图时,有一个尺寸计算的阶段,尺寸计算的时候从根节点宿主容器开始,一级一级往下传递计算,计算结果反向传递回来,最终宿主容器知道整体的宽高。

以宽度计算为例,在这种一级一级传递的过程中,父容器会给出一个当前能用的最大宽度值,并给出一个约束条件(如,明确使用该宽度值、最大不能超过该宽度值、任意宽度),子组件根据父容器给的最大宽度值及约束条件,结合自己的layoutwidth里的值及自身内容,计算出最终的宽度。autodimx、autodimy、autodimdirection是本实施例引入的宽高比计算属性,它的优先级更高,会覆盖layoutwidth和layoutheight的计算结果,具体含义可以参考上述表一。剩下的属性,与padding相关的是组件的内边距,margin相关的是组件的外边距,它们都会影响组件在屏幕里的位置和宽高。

步骤1002,根据所述各个业务组件的尺寸大小,对所述各个业务组件进行排布;

在本申请实施例中,可以按照确定的尺寸大小,在宿主容器组件中对虚拟容器组件和基础组件进行排布。

在具体实现中,布局阶段主要是对子组件按照布局规范去排布。例如,线性布局,就是将子组件从上到下或者从左往右排布;网格布局,就是将子组件按照行列等分的方式排布。所以,布局依赖于步骤1001的尺寸计算结果,只有计算好了子组件的尺寸、子组件的外边距、自身容器的内边距,才能知道每一个组件摆放的位置。

步骤1003,对排布后的各个业务组件进行绘制。

在本申请实施例中,在绘制阶段,如果是虚拟化组件,可以根据尺寸计算阶段和布局阶段得出的结果,在指定的地方绘制内容。正常情况下,绘制需要有一个实体的view。对于虚拟组件来说,它的实体的view就是宿主容器,虚拟化组件最终都绘制到宿主容器这一层上。所以最终看到的只有宿主容器这个视图。如果是原生组件,那只需要调用原生组件对应的绘制方法,由原生组件内部完成绘制逻辑。

在本申请实施例中,对排布后的各个业务组件进行绘制具体可以包括如下子步骤:

s1,获取业务组件模板,所述业务组件模板包含原始模板数据;

s2,将所述原始模板数据编译为目标数据;

s3,对所述目标数据进行解析,以获得相应的目标模板数据;

s4,依据所述目标模板数据,创建业务组件;

s5,获取匹配所述业务组件的业务数据;

s6,将所述业务数据绑定至所述业务组件中。

由于子步骤s1-s6与实施例一种步骤301-步骤306类似,可以相互参阅,本实施例对此不再赘述。

下面以一个具体的示例,说明一下本申请的绘制过程。

如图11所示,是本申请的一种业务组件的层次示意图。通过在宿主容器里,挂载了一个虚拟容器组件、一个虚拟文本组件、两个原生图片组件,可以组合成一个复杂的业务场景下的组件。

如图12所示,是本申请的一种绘制图11所示的业务组件的运行时序图。在开始绘制后,首先触发宿主容器的尺寸计算,给出宽高约束,传递给虚拟容器组件。虚拟容器组件可以根据宿主容器给的最大宽度值及约束条件,结合自己属性内容,计算出最终的宽高。针对虚拟容器组件的宽高约束,可以分别计算虚拟文本组件和原生图片组件的宽高。然后,计算结果可以反向传递回来,最终宿主容器知道整体的宽高。

在触发布局后,可以按照尺寸计算阶段计算好的各个组件的尺寸等信息,首先在排布虚拟文本组件和原生图片组件,再排布虚拟容器组件。当将各个组件都摆放在相应的位置后,触发绘制。当它被最终绘制出来的时候,只看到宿主容器和两个原生图片组件,而且看到的是宿主容器下直接挂载了图片组件。

如果采用常规的方法开发,对于图12的这种布局结构,就能看到宿主容器、一层布局、一个文本、两个图片,而且总共有3层结构。本申请实施例能够通过视图结构扁平化、虚实结合的方式搭建视图。

需要说明的是,对于方法实施例,为了简单描述,故将其都表述为一系列的动作组合,但是本领域技术人员应该知悉,本申请实施例并不受所描述的动作顺序的限制,因为依据本申请实施例,某些步骤可以采用其他顺序或者同时进行。其次,本领域技术人员也应该知悉,说明书中所描述的实施例均属于优选实施例,所涉及的动作并不一定是本申请实施例所必须的。

参照图13,示出了本申请的一种客户端视图的绘制装置实施例一的结构框图,具体可以包括如下模块:

业务组件模板获取模块1301,用于获取业务组件模板,所述业务组件模板包含原始模板数据;

原始模板数据编译模块1302,用于将所述原始模板数据编译为目标数据;

目标数据解析模块1303,用于对所述目标数据进行解析,以获得相应的目标模板数据;

业务组件创建模块1304,用于依据所述目标模板数据,创建业务组件;

业务数据获取模块1305,用于获取匹配所述业务组件的业务数据;

业务数据绑定模块1306,用于将所述业务数据绑定至所述业务组件中。

在本申请实施例中,所述业务组件模板获取模块1301具体可以包括如下子模块:

xml模板获取子模块,用于获取预先编写的可扩展标记语言xml模板。

在本申请实施例中,所述原始模板数据编译模块1302具体可以包括如下子模块:

节点遍历子模块,用于遍历所述原始模板数据的各个节点,所述各个节点分别具有相应的名称和多个属性;

基础组件编译器创建子模块,用于根据所述各个节点的名称,创建对应的基础组件编译器;

属性获取子模块,用于获取所述各个节点的全部属性;

属性处理子模块,用于采用所述基础组件编译器对所述全部属性进行处理,获得多个属性值;

属性值写入子模块,用于将所述多个属性值写入预先创建的文件对象中。

在本申请实施例中,所述多个属性值包括int整型属性值、float浮点型属性值、string字符串型属性值,和/或,表达式类型属性值,所述预先创建的文件对象包括组件区、字符串区,和表达式区;所述属性值写入子模块具体可以包括如下单元:

属性值分类单元,用于按照所述整型属性值、浮点型属性值、字符串型属性值,和/或,表达式类型属性值对所述多个属性值进行分类,生成key-value字节数组;

属性值转换单元,用于分别将所述字符串型属性值和表达式类型属性值转换为对应的哈希码值hashcode;

key-value字节数组写入单元,用于将所述key-value字节数组写入所述组件区;

哈希码值写入单元,用于将所述字符串型属性值和表达式类型属性值对应的哈希码值分别写入所述字符串区和所述表达式区。

在本申请实施例中,所述预先创建的文件对象还包括数据区,所述原始模板数据编译模块1302还可以包括如下子模块:

扩展数据写入子模块,用于若存在扩展数据,将所述扩展数据写入所述数据区。

在本申请实施例中,所述原始模板数据编译模块1302还可以包括如下子模块:

文件对象创建子模块,用于创建文件对象;

写入子模块,用于在所述文件对象中写入文件格式标记信息、版本号数据,以及,各个区域的占位空间信息,所述各个区域包括组件区、字符串区、表达式区,以及,数据区;

业务组件模板解析器创建子模块,用于创建业务组件模板解析器。

在本申请实施例中,所述业务组件模板为xml模板,所述业务组件模板解析器为xml解析器,所述节点遍历子模块具体可以包括如下单元:

节点遍历单元,用于采用所述xml解析器按照xml格式遍历所述xml模板的各个节点。

在本申请实施例中,所述装置还可以包括如下模块:

目标数据加载模块,用于将所述目标数据加载至客户端。

在本申请实施例中,所述目标数据加载模块具体可以包括如下子模块:

目标数据包生成子模块,用于根据所述目标数据,生成目标数据包;

目标数据包加载子模块,用于通过预设代码,将所述目标数据包加载至客户端。

在本申请实施例中,所述目标数据加载模块具体可以包括如下子模块:

目标数据发送子模块,用于发送所述目标数据至预置的模板管理平台;

目标数据更新子模块,用于客户端通过所述模板管理平台在线更新所述目标数据。

在本申请实施例中,所述目标数据解析模块1303具体可以包括如下子模块:

文件数据校验子模块,用于校验所述文件对象中的数据是否为非法数据;

区域数据确定子模块,用于当所述文件对象中的数据不为非法数据时,分别确定所述文件对象的各个区域中的数据的起始位置和字节长度;

区域数据获取子模块,用于依据所述起始位置和字节长度,分别获取所述各个区域中的数据。

在本申请实施例中,所述文件数据校验子模块具体可以包括如下单元:

文件数据校验单元,用于校验所述文件对象中的数据的字节长度和版本号数据是否满足预设要求;若所述文件对象中的数据的字节长度和版本号数据满足预设要求,则确定所述文件对象中的数据不为非法数据。

在本申请实施例中,所述区域数据确定子模块具体可以包括如下单元:

组件区数据确定单元,用于确定所述组件区中的数据在所述文件对象中的起始位置,和所述组件区中的数据的字节长度;和/或,

字符串区数据确定单元,用于确定所述字符串区中的数据在所述文件对象中的起始位置,和所述字符串区中的数据的字节长度;和/或,

表达式区数据确定单元,用于确定所述表达式区中的数据在所述文件对象中的起始位置,和所述表达式区中的数据的字节长度;和/或,

扩展区数据确定单元,用于确定所述扩展区中的数据在所述文件对象中的起始位置,和所述扩展区中的数据的字节长度。

在本申请实施例中,所述区域数据获取子模块具体可以包括如下单元:

组件名字符串获取单元,用于依据所述组件区中的数据在所述文件对象中的起始位置,和所述组件区中的数据的字节长度,获取组件名字符串;

组件数据起始位置确定单元,用于确定所述组件区中的数据中,除所述组件名字符串外,其他的组件数据的起始位置;

组件名读取单元,用于根据所述组件名字符串,读取出相应的组件名;

第一映射关系建立单元,用于建立组件名与组件数据的起始位置之间的映射关系。

在本申请实施例中,所述区域数据获取子模块具体可以包括如下单元:

字符串个数获取单元,用于依据所述字符串区中的数据在所述文件对象中的起始位置,和所述字符串区中的数据的字节长度,获取字符串个数;

字符串资源读取单元,用于根据所述字符串个数,循环读取每个字符串资源,获得字符串索引;

字符串生成单元,用于根据所述字符串资源,生成字符串;

第二映射关系建立单元,用于分别建立字符串索引与字符串之间的映射关系。

在本申请实施例中,所述区域数据获取子模块还可以包括如下单元:

第二映射关系存储单元,用于将所述字符串索引与字符串之间的映射关系存储至预置的字符串资源池中。

在本申请实施例中,所述区域数据获取子模块具体可以包括如下单元:

表达式个数获取单元,用于依据所述表达式区中的数据在所述文件对象中的起始位置,和所述表达式区中的数据的字节长度,获取表达式个数;

表达式资源读取单元,用于根据所述表达式个数,循环读取每个表达式资源,获得表达式索引;

表达式对象生成单元,用于根据所述表达式资源,生成表达式对象;

第三映射关系建立单元,用于分别建立表达式索引与表达式对象之间的映射关系。

在本申请实施例中,所述区域数据获取子模块还可以包括如下单元:

第三映射关系存储单元,用于将所述表达式索引与表达式对象之间的映射关系存储至预置的表达式资源池中。

在本申请实施例中,所述业务组件创建模块1304具体可以包括如下子模块:

组件名获取子模块,用于获取待创建的业务组件的组件名;

目标模板数据获取子模块,用于根据所述待创建的业务组件的组件名,获取相应的目标模板数据;

业务组件构造子模块,用于采用所述目标模板数据,构造业务组件。

在本申请实施例中,所述组件名获取子模块具体可以包括如下单元:

组件名接收单元,用于接收用户传入的待创建的业务组件的组件名。

在本申请实施例中,所述目标模板数据获取子模块具体可以包括如下单元:

组件数据查找单元,用于根据所述待创建的业务组件的组件名,和所述组件名与组件数据的起始位置之间的映射关系,查找相应的组件数据。

在本申请实施例中,所述业务组件构造子模块具体可以包括如下单元:

字节读取单元,用于依次读取所述组件数据中的各个字节;

节点类型获取单元,用于当读取到的当前字节的标记信息为节点开始标记时,获取所述当前字节的节点类型;

基础组件实例创建单元,用于根据所述当前字节的节点类型,创建基础组件实例;

属性值读取单元,用于依次读取所述组件数据中的多个属性值;

属性值信息发送单元,用于根据key-value字节数组,将所述多个属性值对应的信息发送至所述基础组件实例中对应的属性。

在本申请实施例中,所述属性值读取单元具体可以包括如下子单元:

属性值读取子单元,用于依次读取所述组件数据中的整型属性值、浮点型属性值、字符串型属性值,和/或,表达式类型属性值。

在本申请实施例中,所述属性值信息发送单元具体可以包括如下子单元:

属性值数量确定子单元,用于确定当前属性值的数量;

第一属性值读取子单元,用于若所述当前属性值为整型属性值或浮点型属性值循环,则从所述key-value字节数组中读取所述整型属性值或浮点型属性值对应的信息;

第一属性值发送子单元,用于将所述整型属性值或浮点型属性值对应的信息发送至基础组件实例中对应的属性。

在本申请实施例中,所述属性值信息发送单元还可以包括如下子单元:

第二属性值获取发送子单元,用于若所述当前属性值为字符串型属性值或表达式类型属性值,则根据所述字符串索引或表达式索引,从字符串资源池或表达式资源池中获取对应的信息,并发送至所述基础组件实例中对应的属性。

在本申请实施例中,所述业务组件构造子模块还可以包括如下单元:

基础组件节点确定单元,用于当读取到的当前字节的标记信息为节点结束标记时,确认是否存在上次创建的基础组件节点;

容器类型确定单元,用于当存在上次创建的基础组件节点时,确定所述上次创建的基础组件节点是否为容器类型;若是,将当前节点添加至所述上次创建的基础组件节点中;若否,结束所述业务组件的创建;

组件创建结束单元,用于当不存在上次创建的基础组件节点时,结束所述业务组件的创建。

在本申请实施例中,所述业务组件构造子模块还可以包括如下单元:

布局参数对象设置单元,用于为所述基础组件实例设置布局参数对象。

在本申请实施例中,所述业务数据绑定模块1306具体可以包括如下子模块:

基础组件确定子模块,用于确定所述业务组件中待绑定数据的基础组件;

当前属性确定子模块,用于确定所述待绑定数据的基础组件中待绑定数据的当前属性;

当前属性目标值判断子模,用于判断从缓存中是否可获取到所述当前属性的目标值,所述缓存通过对所述当前属性的解析获得;

目标值赋值子模块,用于若是,则根据所述当前属性的类型,将所述目标值赋值给基础组件实例对应的属性;

目标值获取子模块,用于若否,则从业务数据中获取所述当前属性的目标值并缓存,将所述目标值赋值给基础组件实例对应的属性。

在本申请实施例中,所述业务数据绑定模块1306还可以包括如下子模块:

待清理缓存标记确定子模块,用于确定所述当前属性中是否包括待清理缓存标记;

数据值缓存清理子模块,用于若是,清理所述当前属性中的数据值缓存;

调用子模块,用于若否,调用所述当前属性目标值判断子模。

在本申请实施例中,所述装置还可以包括如下模块:

业务组件展现模块,用于在客户端中展现所述业务组件。

参照图14,示出了本申请的一种客户端视图的绘制装置实施例二的结构框图,具体可以包括如下模块:

尺寸大小确定模块1401,用于当接收到绘制客户端视图的指令时,确定各个业务组件的尺寸大小;

业务组件排布模块1402,用于根据所述各个业务组件的尺寸大小,对所述各个业务组件进行排布;

业务组件绘制模块1403,用于对排布后的各个业务组件进行绘制。

在本申请实施例中,所述业务组件绘制模块1403具体可以包括如下子模块:

业务组件模板获取子模块,用于获取业务组件模板,所述业务组件模板包含原始模板数据;

原始模板数据编译子模块,用于将所述原始模板数据编译为目标数据;

目标数据解析子模块,用于对所述目标数据进行解析,以获得相应的目标模板数据;

业务组件创建子模块,用于依据所述目标模板数据,创建业务组件;

业务数据获取子模块,用于获取匹配所述业务组件的业务数据;

业务数据绑定子模块,用于将所述业务数据绑定至所述业务组件中。

在本申请实施例中,所述业务组件包括宿主容器组件、虚拟容器组件,和/或,基础组件;所述尺寸大小确定模块1401具体可以包括如下子模块:

尺寸约束条件确定子模块,用于当接收到绘制客户端视图的指令时,分别确定宿主容器组件、虚拟容器组件,和/或,基础组件的尺寸约束条件;

容器组件尺寸大小确定子模块,用于根据所述宿主容器组件和虚拟容器组件的尺寸约束条件,确定宿主容器组件和虚拟容器组件的尺寸大小;

基础组件尺寸大小确定子模块,用于根据所述虚拟容器组件的尺寸大小和基础组件的尺寸约束条件,确定各个基础组件的尺寸大小。

在本申请实施例中,所述业务组件排布模块1402具体可以包括如下子模块:

业务组件排布子模块,用于按照确定的尺寸大小,在所述宿主容器组件中对所述虚拟容器组件和基础组件进行排布。

在本申请实施例中,所述基础组件可以包括如下的一种或多种:

虚拟文本组件、原生文本组件、虚拟图片组件、原生图片组件、虚拟线条组件、原生线条组件、虚拟进度条组件、虚拟图形组件、原生翻页布局容器组件、原生滚动布局容器组件、虚拟帧布局容器组件、虚拟比例布局容器组件、虚拟网格布局容器组件、原生网格布局容器组件、虚拟线性布局容器组件、原生线性布局容器组件。

对于装置实施例而言,由于其与方法实施例基本相似,所以描述的比较简单,相关之处参见方法实施例的部分说明即可。

本说明书中的各个实施例均采用递进的方式描述,每个实施例重点说明的都是与其他实施例的不同之处,各个实施例之间相同相似的部分互相参见即可。

本领域内的技术人员应明白,本申请实施例的实施例可提供为方法、装置、或计算机程序产品。因此,本申请实施例可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本申请实施例可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、cd-rom、光学存储器等)上实施的计算机程序产品的形式。

在一个典型的配置中,所述计算机设备包括一个或多个处理器(cpu)、输入/输出接口、网络接口和内存。内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(ram)和/或非易失性内存等形式,如只读存储器(rom)或闪存(flashram)。内存是计算机可读介质的示例。计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(pram)、静态随机存取存储器(sram)、动态随机存取存储器(dram)、其他类型的随机存取存储器(ram)、只读存储器(rom)、电可擦除可编程只读存储器(eeprom)、快闪记忆体或其他内存技术、只读光盘只读存储器(cd-rom)、数字多功能光盘(dvd)或其他光学存储、磁盒式磁带,磁带磁磁盘存储或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括非持续性的电脑可读媒体(transitorymedia),如调制的数据信号和载波。

本申请实施例是参照根据本申请实施例的方法、终端设备(系统)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理终端设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理终端设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。

这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理终端设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。

这些计算机程序指令也可装载到计算机或其他可编程数据处理终端设备上,使得在计算机或其他可编程终端设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程终端设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。

尽管已描述了本申请实施例的优选实施例,但本领域内的技术人员一旦得知了基本创造性概念,则可对这些实施例做出另外的变更和修改。所以,所附权利要求意欲解释为包括优选实施例以及落入本申请实施例范围的所有变更和修改。

最后,还需要说明的是,在本文中,诸如第一和第二等之类的关系术语仅仅用来将一个实体或者操作与另一个实体或操作区分开来,而不一定要求或者暗示这些实体或操作之间存在任何这种实际的关系或者顺序。而且,术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、物品或者终端设备不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、物品或者终端设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的过程、方法、物品或者终端设备中还存在另外的相同要素。

以上对本申请所提供的一种客户端视图的绘制方法和一种客户端视图的绘制装置,进行了详细介绍,本文中应用了具体个例对本申请的原理及实施方式进行了阐述,以上实施例的说明只是用于帮助理解本申请的方法及其核心思想;同时,对于本领域的一般技术人员,依据本申请的思想,在具体实施方式及应用范围上均会有改变之处,综上所述,本说明书内容不应理解为对本申请的限制。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1