什么做网站统计好,脑白金广告,免费动态素材网站,网络推广器UI开发的新时代----认识迅雷界面引擎 第一部分#xff1a;交互开发技术概述软件产品的交互开发一直以来都不是一件令人愉悦的事情。首先#xff0c;由于每个人编写的第一个图形应用程序就已经使用了一些交互开发技术#xff0c;而且由于IDE工具的强大,容易总结出交互开发就是…UI开发的新时代----认识迅雷界面引擎 第一部分交互开发技术概述 软件产品的交互开发一直以来都不是一件令人愉悦的事情。首先由于每个人编写的第一个图形应用程序就已经使用了一些交互开发技术而且由于IDE工具的强大,容易总结出交互开发就是“拖拖控件改改属性写写响应”的经验所以很容易被认为是没什么技术含量的工作。但实际上这是一个特别不容易的工作因为作为软件产品的脸面上至公司老板下至任意一个普通用户大家都可以对你的工作成果拼头论足并提出修改意见,而这些修改意见反应到产品的方案修改上总是会让修改成本与项目的修改复杂度不呈线性关系。很多刚刚从事这一行的项目经理总是不能理解为什么按一个方案修改交互需要1天而另一个看起来似乎差不多的方案却要改上一个月 从另一个角度也可以发现这里的技术门槛不易其它领域的设计模式经验总结文章已经汗牛充栋各种各样的新思路和在这些思路上建立起来的各种开源库每过几年基本就要洗上一轮但在交互开发上除了各个厂商提供的平台开发方法几乎没有被公认和被广泛使用的界面开发库更不要提相关方法论和模式的升华总结与创新了。 但是软件产品的交互体验特别是互联网产品的交互体验如今越来越多的受到人们的重视。从过去能实现功能完成交互,到后来提供一致的操作习惯流畅的实现整个交互流程如今还渴望交互体验能基于用户的使用心理设计更加美观并提高产品的整体品味。如今全球市值最高的Apple公司正是靠着其ipod,iphone,ipad系列产品的优秀交互体验和近乎完美的工业设计征服了广大用户。 迅雷作为中国非常流行的一款Windows平台下的客户端产品对于改进产品的交互体验有着强烈的愿望。迅雷5.8完成了工具软件的关键功能提升随后的版本进化希望能进一步改进产品的交互体验 更美观也更现代。迅雷5.9迅雷6是基于传统Windows界面开发技术改进交互体验的结果这个结果虽然还不错但带来了另一个问题开发成本的提高。迅雷作为一款由多个部门合作开发的包括合作开发交互客户端产品传统的开发技术不能很好的组合各个部门开发的模块开发成本与稳定性都有问题。公司迫切的需要一个能解决这些问题的下一代界面引擎并在多个方面使用各种方式开始了勇敢的尝试。 第二部分 过去我们如何开发按钮 现在我将通过一个经典的windows下”自画按钮”的工程实例,和大家一起观察UI开发的过程。”自画按钮”是一个非常常见的产品需求,相信不少读者都有过相似的经历下列故事纯属虚构如有雷同真是巧合. 那年正是Windows XP最流行的时候我们的产品也跟进时代的发展,将界面风格升级到了XP Style,同时我们的产品还兼容Windows2000,设计和产品都希望我们的软件在两个系统下都能有一致的外观表现。于是,我就需要开发一个能在两个平台下都长得一样的按钮。这个活并不困难,我们先看看需求: 看了需求之后作为一个合格的Windows开发工程师很快就完成了。实现代码大致如下: CButton::OnPaint() { DrawBkg(m_state,0,0,width,height); DrawText(m_state,m_btnText,width/2-textWidth/2,height/2-textHeight/2); if (isFocus) { DrawFocusRect(2,2,width-2,height-2); } } CButton::OnLButtonDown(){ ChangeState(BTN_STATE_DOWN); } CButton::OnLButtonUp(){ ChangeState(BTN_STATE_NORMAL); FireEventOnClick(); } 分析实现代码,大家可以看到基本思路是这样的 1. 确定按钮有几个状态然后根据这些状态下按钮的外观确定如何实现OnPaint函数。 2. 处理WM_LBUTTONDOWN,WM_MOUSEMOVE等输入消息在这些消息里改按钮的状态 3. 在WM_LBUTTONUP,WM_KEYUP等消息里Fire一个按钮自己定义的OnClick事件 很好我们的软件用上了新按钮问题解决了产品经理和设计师都很满意。咱也结束了一天的工作安心回家睡个好觉。 又过了几天公司的产品总监在产品会上提到我们要统一所有产品的交互逻辑特别是某些按钮根据现在的交互规范应该加上醒目的图标。会后设计师打开PHOTOSHOP很快就把按新规范调整的按钮发给了我。如下图 这次我没有立刻开始实现因为公司的另一产品也用到了我实现的这个按钮我要保证原有的按钮能继续正常工作我打算和那边的研发商量一下怎么改。这里我有两个方案。 方案一 1.定义 CIconButton继承CButton 2.CIconButton添加方法SetIcon 3.在CIconButton::OnPaint里添加如下代码 OnPaint() {DrawBkg(m_state); DrawIcon(m_state);DrawText(m_state); } 4.使用新的CIconButton完成需求 方案二、 1.在CButton里添加两个扩展点回调 OnPaint() {if(OnDrawBkgCallback()) DrawBkg(m_state); if(OnDrawItemCallback()) DrawText(m_state); } 并提供设置回调的接口 SetOnDrawBkgCallback() SetOnDrawItemCallback() 2.产品通过SetOnDrawItemCallBack完成需求 void OnBtnDrawItemCallback() { DrawIcon(); return true; } m_button.SetOnDrawItemCallback(OnBtnDrawItemCallback); 在方案的选择上大家有了一些争议方案一看起来比较直接而且实现起来也比较快最重要的是使用我的控件库的产品开发工程师特别希望这么改而方案二实现需要更多的代码并且产品开发工程师需要学习和编写更多的代码才能完成这个需求不过这个方案的未来我更看好。最后我们选定了一个方案(在这里选哪一个都能完成需求)加班写好代码提交测试发布新的界面控件库制作新的安装包一阵忙碌这个小小的需求升级总算是结束了。 又过了几天公司大老板通过邮件告诉大家,公司产品的大客户比较喜欢Windows Rabbion风格。于是产品经理再次调整设计,要求一些按钮要改成如下样式: 很明显这次图标跑到按钮文本上面去了这就是最新的时代潮流。虽然很不情愿但这不就是生活么隔壁卡位的设计师半个小时搞定了所有需要调整的地方的效果图按时下班回家了而我们工程师还必须加班! 这次的工作量就和上次修改的方案有关了 如果我们上一步使用了方案一这次我们日子没那么好过了。我们需要创建一个新的CIconButton2继承自CIconButton,再加一个新接口SetIcon2可以在设置图标的同时调整图标在文本的什么位置这次还耍了个小聪明这个接口可以支持图标在文本的上下左右4个方位 。 而使用方案二的优越性则在这里得到了体现作为按钮的开发者并不需要修改一行代码你只要继续指导一下需要实现这个需求的产品开发工程调整一下OnBtnDrawItemCallback的实现即可。 实际上在迅雷我们有过一次在方案一这条路上走到头的经历迅雷的代码里有一个叫 CXLButton的怪物提供了近200个接口有快2万行代码。当编写这个类的哥们走人以后接手这个Class的新人发现这个按钮的用途(在最后一个版本的需求里)只是用来实现一个图标在文字上面的工具栏按钮”这垃圾代码是谁写的”他逢人就要这么嘲笑一下然后花了一个下午的时间写了一个新类 CXLButton2清晰明了的实现了这个需求。但我们都知道这是另一个轮回的开始。而且有资格使用方案一还意味着控件开发工程师愿意帮助产品开发工程师修改接口与实现如果这两组工程师不在一个公司更大的成本会花在人与人的沟通上甚至无法进行。 Microsoft提供的标准控件通过类似方案2的方法提供可扩展性。方案二在过去都被普遍认为是一个出色的解决可复用性问题的模式我很少在招聘的时候能看到有人能在开发控件的时候通过这种方式提供可扩展性但其缺点之一就是让控件的使用有点复杂学习成本很高。比如我至今都没有通过MSDN 完全搞清楚过微软TreeView提供的这种扩展事件的细节还好有一份泄漏的Windows源代码让我对这些回调之间的关系能有清楚的认识。而且这个方案会让产品开发工程师学习很多高度依赖控件库内部实现的知识如果产品更换的底层界面库那么大量的这种经验就没有意义了。 而且方案二真的能以不变应万变支持所有的需求变化么大家可以自行思考。 通过这个可扩展的按钮控件的开发实例,我们总结了三个交互开发的经验: 一、 交互是最易改变的需求 二、 开发一个可复用的交互控件的主要工作有1)根据最初的需求决定方法事件2)根据最初的需求决定如何实现OnPaint,如何将原始的OS输入事件转成逻辑事件3)在OnPaint里织入扩展点。 三、交互开发涉及的技术点很多实现时注意细节注规避常见问题当绘制特别复杂的时候还要仔细优化性能。 第三部分 新的思路 从头观察上面的例子我发现: 不管怎么修改交互需求隔壁卡位的设计师总是很少加班他们的工作量似乎能很好的和交互需求的修改量成正比而且没有什么局限。相反我们的工作总是存在一些天花板一旦需求改变越过了开发时预设的一些前提条件需求修改的响应时间会大幅增加。 不管是通过那种方法有限的接口和有限的扩展点理论上满足不了近乎无限的需求变化。而我们观察所谓的通过事件扩展控件的方案其核心是希望OnPaint能够根据各种参数调整工作流程。而OnPaint里实现的功能说到底就是 “在指定位置使用适当的函数绘制文本或贴图”。要想让一段代码的流程具有很强的可变性是非常困难的。从某种意义上说OnPaint函数就是万恶之源。 明白了问题的根源那么如何解决问题就有了方向:我们要在交互开发的过程中干掉OnPaint。实际上在这之前已经有技术实现了这一点HTML本身并没有提供绘制函数而实际上前端交互开发是目前所有交互开发技术里流程分工合理并且需求修改响应速度保持线形增长容易学习有完善工具链条的技术。我们可以在传统app开发里使用HTML技术么 很多人回答YES,并做出了开创性的尝试。不过经过了一些思考之后我们并没有选择HTML原因我们以后会在另外的文章里详细的介绍。经过了一些借鉴和总结我围绕这个问题提出了一个新的概念可以通过定义原子UI对象(UIObject)之间的父子关系和位置关系组成对象树(UIObjTree)来描述界面,UIObject的类型是相当有限的。而按逻辑构建的UIObjTree的实际上组织了OnPaint里“按什么样的顺序在什么地方画什么”的问题。 这个概念有点抽象还是你已经完全明白了都没关系还是刚刚例子里的按钮按这种方法分析以后我们会得到一个怎样的UIObjTree呢 非常直观的对象树只有两个UIObject。接下来的需求修改就变成了修改这个数据结构。而树结构的修改接口提供起来是非常简单缺完备的添加节点删除节点查找节点修改节点属性。我们看看在这个结构上如何响应上面的需求 你会发现抽象UI得到的UIOjbTree会与设计师提供的PSD图结构非常相似这就非常符合真正的开发情况开发得到的需求并不直接来自产品经理而是来自设计师。如果技术能够将设计师的成果直接转化成App可以使用的数据结构那么这样的技术无疑在实现能力和开发效率上能与设计师相同。这个理论迅雷通过迅雷7 XMP多个产品的大规模实践已经得到了有效的证明所以BOLT界面引擎的创新根本上是提出了一种新的抽象交互的思路。 这里建议您可以考虑将一些常见的控件或则您现在正在开发的软件的交互解构成UIObjTree试一试吧。 围绕这个概念来构建整个界面引擎我们还获得了一系列进一步的好处 基于同样的模式分析交互能输出近乎一致的结果。 可以将界面布局从逻辑代码中独立出来。 对界面的抽象是平台无关的。(虽然我们没有把跨平台作为BOLT界面引擎的关键目标) 让对象的属性在一段时间里按规律变化就能实现动画。开发各种特效也有了一致的思路 基于对象树枝叶嫁接的复用和界面模块划分易于理解。并提供了统一的可访问性 (所谓的可访问性是指你可以在控件的开发者不提供实现的基础之上只通过UIObjTree的树操作借口就能访问并修改其表现) 交互开发技术的变迁 整理上面的思路我们得到交互开发技术或则就是界面库的分代标准 第一代 :SDK开发,使用系统默认控件 第二代 :基于窗口子类化的自绘控件皮肤库。绘制通常基于GDI。控件类型和系统一致。 第三代 :提供一套完整的体系(窗口,绘制等)所有的控件都基于这个体系开发。本身提供很多功能更强的控件并有统一的方法开发/使用新控件。这一代库主要是解决实现能力问题。由于Windows的系统限制二代库有很多功能局限。(比如按钮发光这个需求需要按钮的实现代码可以绘制到按钮子窗口以外的) 第四代 :布局文件脚本语言的开发模式依靠”组合”抽象界面并围绕这个概念搭建 。不提供控件但提供易学易用的控件开发模式。不提供内置特效但能让使用者轻易开发自己的界面特效。 这个分代标准基本上是参考windows上的界面库发展而制定的第二代和第三代库本身其实没有什么本质上的区别主要是Windows由于兼容原因提供的UI相关API和窗口混和器过于古典子窗口与父窗口之间只有遮盖关系没有办法混合很多GDI函数都无视Alpha通道使得开发一套完整的界面库反而还需要自己实现窗口管理和图形库这些有一定技术门槛的基础设施。新的OS本身提供的UI框架本身就解决了这些问题所以这些新的OS上的库就直接是3代库了。 需要指出的是,有一些技术框架也提供基于组合的方法构建界面的方法.比如现在相当流行的iOS上的cocoa框架很多时候可以只通过组合各种View的方法来创建新的控件。但这个框架我依旧认为他是三代库因为框架还是依旧以绘制为核心构建比如你可以组合多个不同属性的按钮构成一个新控件但你无法获得按钮上文字的SubView,没有获得统一的可访问性。 第四部分 Bolt界面引擎概念介绍 关键概念 • 使用XML文件来定义UIObjTree用Lua脚本来实现界面逻辑 • UIObject的类型包括ImageObject,TextObject,还有一些精心设计的原子类型 《引擎内置的元对象介绍》 • 围绕核心概念建立了一系列辅助设施 基本工作原理 BOLT界面引擎这里提供了两个重要的核心概念HostWnd ,Render。Render可以把一颗构建完成的UIObjTree渲染成一张位图。而HostWnd是界面引擎核心与操作系统之间的桥梁能把这张位图通过系统提供的API画到屏幕上并能转化操作系统的键盘/鼠标等事件成为引擎的定义得标准输入事件(Action)。 这张图里还提到了我们的UI资源管理模块和XML布局文件读取与管理模块。 Render的基本工作原理 DirtyRect(脏矩形)是驱动Render工作的核心。关于脏矩形在很多2D游戏开发的文章里都有详细介绍。当一颗UIObjTree上的产生了脏矩形其对应的Render在下次渲染时就会开始有动作否则就什么也不干。Render会选取与脏矩形相交的所有UIObject然后按这些UIObject的z-order排序从小到大排序 再按这个顺序依次调用这些UIObject的Draw方法。由于所有的UIObject都是由引擎实现的所以这个Draw方法也是一个不可见的内部函数Draw的实现会调用一些我们精心优化过的图形图像绘制函数这一切就构成了Bolt界面引擎的高速渲染引擎。 注:虚线表示的矩形是脏矩形那么Render只会渲染与这个区域相交的UIObject。 BOLT界面引擎里控件的概念 基于UIObject和UIObjTree开发控件就是设计一个可复用的“对象树片断”而使用控件就是由界面引擎完成这个对象树片断如何嫁接到对象树上。 这里通过一个简单的例子演示一下这个过程 这样的一个MessageBox。抽象成UIObjTree 我们可以定义Button的对象树片断是 合并以后 而且很明显引入控件的概念能把一颗复杂的UIObjTree分解成多个片断交给团队开发而最后合成的UIObjTree依旧保持了各个节点的可访问性。 第五部分 未来展望 Bolt界面引擎的核心概念是完全创新与独立的并不依赖任何操作系统。所以BOLT界面引擎的发展方向之一是把界面引擎移植到各种各样的平台目前最成熟稳定的平台是Windows有工业级产品的质量。我们在Andiord平台和MacOS平台都有初步移植的版本但我们还需要花费很多精力在合适的机会完成让这些平台的界面引擎更完美 我们在移植到Andiord平台时发现界面引擎要想在这些手持设备上流程运行原有的基于CPU指令集优化的高速渲染器是不好的。一是CPU性能达不到不够流畅二是太耗电这里迫切需要使用设备提供的硬件加速功能。得益于界面引擎不希望用户编写绘制代码的核心概念我们只需要重新调整高速渲染器就可以实现硬件加速。目前我们正在尝试各种各样的支持硬件加速的框架方案既能高效使用硬件的能力 又能兼顾CPU算法的灵活性。 我们目前的主要精力都放在引擎本身的完善上但实际上如同HTML与Dreamweaver一样界面引擎在设计之初就可以让布局部分由使用专业工具的专业人员完成而不是负责编写逻辑代码的工程师。我们对我们的布局XML格式的简单优雅和可扩展性都有充分的信心我们衷心的希望各位同行在理解认可了我们的概念后支持我们的标准这样大家就能开发各种辅助工具互相通用共同改进BOLT界面引擎的工具链支持。 转自http://xldoc.xl7.xunlei.com/0000000018/00000000180001000020.html转载于:https://www.cnblogs.com/Bonker/p/3286773.html