1.概述
开发一个SDK,我们的第一步当然是先给SDK取个名字呀,由于本人比较喜欢水獭,所以给生成式UI动态化SDK取名为Otter。说到生成式UI,这里我们再次解释下,生成式UI顾名思义就是与用户交互的界面不再由程序员事先编写好,而是由AI大模型经过和用户"交流",根据用户的需求实时生成的精美且能符合用户功能需求的可交互界面。
生成式UI在很多场景下可以简化用户的操作,比如在车机中,用户开车时,如果用手去操作车机上的界面,简易的功能还行,但是涉及到需要操作多步才能完成的场景,则会带来安全隐患,比如开车回家的路上,假设需要事先买点菜。买菜涉及到,选菜,加入购物车,结账等步骤。如果用生成式UI,这一切都会很简单,直接通过语音和AI大模型交流,AI大模型根据用户的需求,实时生成界面反馈给用户。这样用户就可以和语音助手在 "聊天" 的过程中就把事情干完了。
从上面的描述中我们应该可以感觉到生成式UI必须具备的特点,那就是实时性,动态性,可交互性,跨平台。
1.1 生成式UI实时性
实时性要求生成式UI要尽可能快的出现在用户的可见范围内,大约1~3秒内,界面必须展现出来。这就要求描述生成式UI的文件必须尽量小。因为用户和大模型交流时肯定需要云端数据的支持,所以网络带宽一定的情况下,我们下发到用户设备终端的数据应该越小越好,这样展现的速度也会更快。
1.2 生成式UI动态性
何为动态性?动态性即生成式UI不是固定不变的。举个例子,我们的Android端页面,比如美团App中的页面原生部分,都是事先写好的,这个页面只要APP上线,在不修改并且不升级版本的情况下,页面的结构样式是不会变化的,这时我们可以称这种页面不是动态性的。动态性是在不升级版本的情况下,可以动态替换我们已经上线的页面。这个词其实不新鲜,阿里,京东,美团,字节等大厂都会有自己的动态化SDK,用于在Android,IOS,HarmonyOS系统上快速上线需求。简单理解动态性,就是页面UI界面是动态可变的,上线后我们可以在不重新发布版本的情况下修改页面的样式布局和交互
1.3 生成式UI可交互性
可交互性就是要求我们生成的UI界面必须是可以和用户的语音,手势等输入交互的。这点很简单,因为我们生成的UI界面如果不能和用户交互,那么和放一张图片没啥区别。这点不多做赘述。
1.4 生成式UI的跨平台
生成式UI还有一个比较重要的特性,那就是跨平台,即我们生成的UI界面必须要能在Android,IOS,HarmonyOS 上正常展示,并且不同系统上展示的UI界面之间的差异要尽可能小,这样才不会让用户感觉到困惑。所以我们定义的DSL UI界面要能在各个系统上准确的被解析渲染。并且交互行为效果也应该相同
2.DSL整体定义
DSL(Domain-Specific Language,领域特定语言)是一种专门针对特定问题领域设计的编程语言。通常用来表达特定领域的意图和规则,使编写、阅读和维护代码的过程更加高效和直观,让代码更接近自然语言,从而增强代码的可读性和维护性。我们可以有很多方式定义DSL,比如使用HTML,CSS,XML,JSON等来作为DSL的描述语言,目前在业内主要有使用的是CSS,JSON(阿里的GaiaX使用),京东,美团使用的XML。
2.1 DSL描述语言的选取
阿里的GaiaX动态化跨端框架使用的描述语言是CSS+JSON的方式作为DSL的描述语言: 我理解GaiaX之所以选择这种方式的原因是因为写这个框架的作者是做前端开发的。并且这种方式和方便GaiaX提供的GaiaStudio做UI控件的拖拽编辑并且到处的操作。 而我们可以再看下京东使用XML描述的DSL语言模板,我在京东的APP中搞到的模板如下所示:
![在这里插入图片描述](i-blog.csdnimg.cn/direct/45fa... =x500) 上面图片的内容是折叠后的,全部的内容太多了
通过对比上面的两种DSL的描述语言,发现GaiaX框架使用的JSON+CSS的方式,更加适合前端的小伙伴理解,但是界面结构的描述性较差,且需要传递的内容也更多,因为涉及到CSS,databinding,layer 三个文件的内容。而京东使用的XML作为描述语言的方式则结构描述性较好,但是布局容器统一使用FlexboxLayout来描述,通过添加属性的方式去控制子View的布局展示。这种方式感觉容器的职责不太分明。一个XML节点包含了太多的功能。
所以结合GaiaX和京东动态化模板的优缺点,最终决定选取XML作为生成式UI 动态化SDK的DSL描述语言
2.2 使用XML定义动态化模板
动态化模板的固定布局如下:
xml
<Otter>
<Layout>
....
</Layout>
<Events>
<Event funId="changeAttr">
</Event>
</Events>
</Otter>
Otter: 动态化模板的根节点标识 Layout:UI布局节点标识 Events: UI 事件节点标识 Event:UI 事件节点
视图的公共属性如下:
background:背景色 width、height: 宽高 id:组件标识 text: 文字内容 radius: 圆角 borderColor:边框颜色 borderWidth: 边框大小 leftBottomRadius: 左下圆角 leftTopRadius: 左上圆角 rightBottomRadius: 右下圆角 rightTopRadius: 右上圆角
2.2.1 文字组件 Text
xml
<Text
background="MAGENTA"
height="match_parent"
id="text_tip2"
radius="20px"
text="SenseDyne!!!"
textColor="#ffffff"
textAlign="end"
textSize="10px"
width="100px" />
text: 文字内容 textColor:文字颜色 textSize: 文字大小 textAlign:文字对齐方式
2.2.2 图片组件 Image
xml
<Image
borderColor="#ff0000"
borderWidth="5px"
height="200px"
id="img_content"
leftBottomRadius="30px"
leftTopRadius="20px"
placeholder="dyn_placeholder_100x100"
rightBottomRadius="15px"
rightTopRadius="10px"
src="$img"
width="200px" />
src:图片的url地址,可以式本地的地址,也可以式网络图片地址
2.2.3 动画组件 Animation
xml
<Animation
id = "ani_loading1"
radius="15px"
background="transparent"
width = "50px"
height="50px"
repeatCount = "infinite"
repeatMode="restart"
autoPlay = "true"
animationSrc = "assets:animation/lottie-sunny"
/>
repeatCount :动画的重复运行次数 repeatMode:动画重复模式 autoPlay :是否自动播放动画 animationSrc : 动画的资源路径
2.2.4 留白组件 Spacer
在开发中经常需要去控制两个View之间的间距,所以添加一个Spacer组件来作为View之间的间距,如果式竖向的,就设置高为间距距离,如果式横向的,就设置宽为间距距离,Spacer还可以添加背景颜色,作为分割线使用,属性支持视图的公共属性
xml
<Spacer width="match_parent" height = "10px"/>
2.2.5 容器组件
容器组件是可以添加其他视图的组件,并且可以限制其他视图的摆放方式,类似于Android中的ViewGroup,在Android中我们知道有个容器组件是LinearLayout,可以设置orientation属性让其控制它的子View是横排还是竖排,一个容器组件控制两个属性,我觉得职责不单一,分析现在市场上的界面,基本上都是水平, 竖直,重叠摆放,所以在我们的生成式SDK中,我将垂直和水平分成两个组件,分别是Column标识垂直容器,Row标识水平容器,定义如下:
垂直容器:
xml
<Column
alignItems="center"
background="#ffff00"
borderColor="#ff0000"
borderWidth="10"
height="wrap_content"
id="cl_container"
justifyContent="center"
leftBottomRadius="10"
leftTopRadius="30"
rightBottomRadius="20"
rightTopRadius="20"
width="match_parent">
</Column>
alignItems: 子View的对齐方式
水平容器Row:
xml
<Row
alignItems="center"
background="linear-gradient(to bottom, #8b572a 0%,#f5a623 100%)"
height="wrap_content"
id="rw_container"
justifyContent="center"
leftBottomRadius="30px"
leftTopRadius="20px"
rightBottomRadius="10px"
rightTopRadius="20px"
width="match_parent">
</Row>
alignItems: 子View的对齐方式 justifyContent: 子View的对齐方式
alignItems,justifyContent都是设置子view的对齐方式的,justifyContent设置的是主轴方向的对其方式,alignItems设置的是纵轴方向的对齐方式,这里简单了解下就行,后面实现时会细讲
2.2.6 列表容器组件OTList
xml
<OTList data="$data"
height="200px"
id="cl_vertical"
listDirection="vertical"
itemPadding="16px"
width="match_parent">
<!-- Item表示列表每项数据的视图布局 -->
<Item>
<Column
radius="15px"
background="green"
height="wrap_content"
justifyContent="center"
id="row_container"
onClick="$fun{showToast}"
width="match_parent"
alignItems="center">
// ...... 可以像编写其他布局一样编写每个列表子项的UI布局
</Column>
</Item>
</OTList>
data: 列表容器的数据,义JSON格式提供 itemPadding:列表项之间的间距 Item: 标识列表容器中的子项UI,在Item节点下可以利用其它的动态化组件描述出子项的UI界面样式 listDirection: 列表的方向有两个:水平方向和垂直方向
3.当前面临的问题
3.1 大模型目前无法生成DSL定义的界面
当前生成式UI的SDK的DSL虽然定义好了,但是目前大模型还不认识,因为我们的DSL目前没有多少人用,可供训练的数据不足,这也是我开源这个SDK的目的之一,就是希望更多人一起共建,一起使用,给大模型提供更多的训练数据。有的厂商可能会选择react native作为生成式UI的动态化方案。这也不失为一种方式,毕竟react native都支持跨平台,动态下发,而且目前的大模型能很好的生成react native代码,并且有的大模型生成的还比较好。那为啥我还重新弄一个呢,主要原因是react native被设计出来是为了跨平台,不是为了动态化,而生成式UI需要极强的动态化特性,react native的动态化方案是通过将react native 的代码打包成bundle 文件,然后下发到端上渲染,我对比过,同一个UI界面,用XML DSL编写,文件大小就几KB,但是react native 却需要几十甚至几百KB,就算使用分业务包加载的方式,也会有几十KB。而且RN的架构还在变化,与原生混合开发集成环境繁琐。而且会增加包体积,所以我选择放弃react native的方式,选择重新实现一个生成式UI 动态化SDK。
3.2 支持的基础组件还很少
因为生成式UI的SDK刚开始搞,仅仅支持非常基础的组件,比如文字,布局,列表,动画等,像多选框CheckBox,输入框:Input,下拉菜单等都还没支持,需要后面扩充
3.3 缺乏公共模板组件库
目前SDK刚刚起步,还没有建立一个模板组件库,没有一个统一的地方管理我们的模板组件,我们需要有一个统一的地方分类存放我们的动态化模板,方便后面能给到大模型使用
4.总结展望
今天的文章主要介绍了Otter生成式UI 动态化SDK的DSL定义,在文中介绍了生成式UI的定义及使用场景,并且列出了后续需要实现的基础组件。并指出了当前的生成式UI 动态化SDK面临的问题。希望在后面读者能和我一起将这些问题都解决掉,并且思考如何接入3D界面的渲染,如何添加动态化模板的后端管理系统。恳请大家一起热烈交流,如果你们有更好的生成式UI的方案,欢迎和我一起讨论。