基础知识漫谈(2):从设计UI框架开始

说UI能延展出一丢丢的东西来,光java就有swing,swt/jface乃至javafx等等UI toolkit,在桌面上它们甚至都不是主流,在web端又有canvas、svg等等。

基于这些UI工具包\框架,又产生了大量通用的或者业务性的UI框架,比如Draw2d、GEF、easyUI乃至国内的EChart、白鹭等等。

这些框架的业务范围各异,一个程序员的时间和精力有限,你不可能全部都掌握,又不能预言出是哪一个将来会独步天下,甚至,连当前哪一个最流行,都够打一阵嘴炮。

那,我们应该学什么?

本章节谈谈,如果,我们只有一个GC(graphic creator\capability)的时候,如何设计一套UI框架出来,了解了这些,在学习新的UI框架的时候,会更加容易。同时,这里提到的分析思路也可以应用到其他类型框架中,辅助学习。

切记,1、复杂由简单构成;2、构成具备规律

可想而知,再复杂的框架,划拉到底层的时候,也都是你早就应当掌握的基础知识。不同的框架需要的基础知识可能不一样,但是规律(思想)大多是接近的。

我们先来定义一下,什么是GC?

每一个使用过上面提及的UI toolkit的程序员,应该都注意到了,它们很多都有提供“绘制”功能。在SWT中,提供该能力的是org.eclipse.swt.graphics.GC,在H5 canvas中,则是CanvasRender(也就是canvas element#getContext(‘2d’)得到的对象)。

它们有什么共同点?

可以设置ARGB信息,可以调用来绘制图片、线段、形状,等等。

为了方便起见,这里统一称为GC,它是UI框架的基础。

来思考一个问题,一个下图所示的界面,如果用GC自行绘制的话,你要怎么做?

不用想太多,完整实现是不科学的,写到颓,颓到秃。来看下最常见的解决方案吧:

把该界面上的各个部件拆开来,相同特性的归为一类,每一类都提供一个绘制的方法,比如一个按钮,就需要GC绘制四条边、阴影线以及其中的文本。

然后,让遍历所有的部件,让文本的归文本,按钮的归按钮。

有没有突然感觉简单了?这就是所谓的UI框架干的事儿。

如何构建这个UI框架呢?本文采取的思路是:面向对象建模。

具备面向对象知识的你,应当能分析出以下结论:控件,控件的布局,以及各种事件的处理,这三个元素组成了一个基本的UI框架。

1、控件

按钮、文本框等可操作对象以及包裹它们的容器,这里称之为控件。

所以,我们需要创建出以下对象,继承关系以树形结构表示:

其中父子级只表达继承关系,Control和Composite它们的实体关系图,则可能是如下所示:

其中,Control提供两个方法:

paint(GC)负责调用GC,来绘制自身。

getBounds()负责提供当前控件的位置、大小信息,一般包括x,y,width,height。

Composite作为复合控件,它既具备Control的两个方法,用于正确的绘制自身,又具备一个children列表,里面全都装的是Control,当它的paint方法调用的时候,应当迭代自己的所有children,调用它们的paint。

具备此种结构之后,任何UI界面是不是都成为了某种单根结构?

根节点是一个Root Composite,叶子节点则是具体的某个Control。再联想之前的打印窗口,它的实体结构大概会是这样:

来活动活动思维吧,这是什么数据结构?各个控件的paint()方法调用顺序是怎样的?

2、布局

明明是有bounds的,为什么还需要“布局”呢?

其实啊,bounds(x,y,width,height)也可以视为一种布局,我称之为自由布局,这种布局其实并不好,结论太粗暴难以接受么?大概讲解一下:

你需要非常精确的控制x,y,width,height四个变量,设想你要制作一张两行四列的表格,每个单元格你都得控制它们的位置,bounds信息如下所示:

[0,0,100,20],[100,0,100,20],[200,0,100,20],[300,0,100,20],

[0,20,100,20],[100,40,100,20],[200,60,100,20],[300,80,100,20]

如此你可以推论出一个公式,设i为行号,j为列号,单个cell的bounds信息公式为[i*width,j*height,width,height],注意到问题没有,你需要自己维护一个嵌套循环,来为每一个单元格赋值。

控制相对位置需要花费大力气。明确一个事实,在该“自由布局”里,child的范围是有可能超出parent的边框的(因为bounds的x,y目前指代的是GC使用到的x,y,也就是整个画布的基准点),除非,你把每一个child的计算公式都改为[i*width+offsetX,j*height+offsetY,width,height],这里的offset代表parent的绝对位置。

很难控制缩放。比如你要对上述的表格进行缩放,则你需要修改bounds信息,确定缩放的策略,比如整体缩小一个zoom值,列出的公式大概会变成这个样子[i*width*zoom,j*height*zoom,width*zoom,height*zoom]

仅仅说明一个方式不好,并不能证明其他的方式好,很多人已经想到了,我们可以把上面的这些“公式”抽离出来,整理出可复用的代码。如果有其他的布局方案,也可以整理出对应的复用代码,在paint之前应用上去,不就好了么?

对,其实,这个可复用的方案\策略,也就是“布局”。

以伪码说明实现方式:

define Composite{
LayoutManager layoutManager;
/**
*绘制
*/
void paint(){
  layout();
  //dopaint
}
/**
*执行布局
*/
void layout(){
  layoutManager.layout(this);
}
}

define LayoutManager{
void layout(Composite parent){
  int index=0;
  //遍历所有的children,获取它们的layoutData,修改它们的位置
  foreach(Control child:parent.getChildren()){
  //根据child的index以及布局配置计算出偏移量
  offset=computeOffset(index);
  //获取child的布局数据
  LayoutData data=child.getLayoutData();
  //使用布局数据修改child的bounds信息
  data.computeBounds(child,index,offset);
  index++;
}
}
}

从上面伪码我们可以看出,控件具备layoutData这个成员函数,容器(复合控件)具备layout这个成员函数.

layout用于规定该容器内部的布局类型(比如网格类型GridLayout),整体的布局规划(体现在上述代码中的offset对象,为下一个需要布局的child提供偏移量)。

layoutData负责具体的某一个child在整体中的排布。

由于封装在layout和layoutData中的都是算法(体现在computeXXX方法中),所以,我们可以灵活的规定、服用不同的组合方式,比如把一个控件布置在容器的整体居中位置。

完成了这些,你的UI框架就能用于展示各种视图了。

3、事件分发

仅仅用于展示自然是不够的,不然UI框架完全可以称之为视图框架,这个UI框架应当可以接收各种类型的键盘、鼠标输入。

以H5的canvas为例,我们知道canvas是可以添加鼠标\键盘监听的,你完成了控件和布局,点击按钮控件,响应事件的是按钮还是canvas?

自然是canvas,浏览器哪里知道你写了个“按钮”出来。

对于不熟悉MVC模式的同学可能会有些疑惑,一个“绘制”上去的假按钮,要如何响应事件呢?

我们再来看下这棵树:

推论如下:

1、根容器的bounds等同于canvas的位置大小。

2、如果canvas接收到了鼠标事件,鼠标一定位于某个根容器下某个控件位置上。

3、树遍历控件,即可快速找到事件发生的时候,鼠标处于哪个控件之上。

鉴于JS在某些浏览器上的执行效率(我不是说微信),我们还可以做更多的优化。这里抛砖引玉:

1、引入(layer)的概念,对树结构再做一个横向划分,优先查找层数高的控件,也可以让某些层的控件不参与查找。

2、对控件本身设置可点击属性,毕竟if判断的速度要比计算(x,y)是否位于bounds范围内要快。

下一篇我来讲讲,如何制作游戏\动画框架,再以后应该不会谈UI相关的东西了,毕竟我也不是做这个的。

时间: 2024-10-13 05:37:13

基础知识漫谈(2):从设计UI框架开始的相关文章

JAVA 蔡羽 基础知识漫谈

基础知识漫谈(1): 想到哪儿写到哪儿 http://www.cnblogs.com/anrainie/p/5606570.html 基础知识漫谈(2):从设计UI框架开始 http://www.cnblogs.com/anrainie/p/5609958.html 基础知识漫谈(3) 组合基础知识,设计游戏框架 http://www.cnblogs.com/anrainie/p/5614461.html 基础知识漫谈(4):讲讲元数据 http://www.cnblogs.com/anrain

[转]基础知识漫谈(1): 想到哪儿写到哪儿

[转]基础知识漫谈(1): 想到哪儿写到哪儿 本文转自(http://www.cnblogs.com/anrainie/p/5606570.html) 一.想到哪儿写到哪儿 给公司新员工培训,和网上的新手做交流,我最先强调的都是基础. 基础有什么用? 1.节省沟通成本 有天,java群里来了个新人,上来就提了一个问题: “我代码跑不起来,怎么办?” 这一看就是还没入门,没办法提供具体的信息. 于是有个有耐心的老鸟出来了,开始了一连串提问:异常栈看一下?有编译期异常吗?贴出你的main函数看看?

基础知识漫谈(1): 想到哪儿写到哪儿

一.想到哪儿写到哪儿 给公司新员工培训,和网上的新手做交流,我最先强调的都是基础. 基础有什么用? 1.节省沟通成本 有天,java群里来了个新人,上来就提了一个问题: “我代码跑不起来,怎么办?” 这一看就是还没入门,没办法提供具体的信息. 于是有个有耐心的老鸟出来了,开始了一连串提问:异常栈看一下?有编译期异常吗?贴出你的main函数看看? 新人收到了问题并且抛出了你都在说什么异常. “你还是截图吧.”老鸟说. 这里涉及到了异常栈,编译期,main函数等等再基础不过的知识,有那么部分毫不客气

Java基础知识强化103:JSON解析框架汇总

1.Gson Gson是Google提供的一个能够将Java对象转换成相应JSON表达形式的一个开源Java类库,当然用Gson也能将JSON字符串转换成与之等价的Java对象.Gson对于任何Java对象都有效,包括那些预先存在没有源代码的对象. 现在已经有一些能将Java对象转换成JSON的开源项目了.但是大多数项目都要求你在类文件中加入Java注解,而当你无法改动源代码的时候这是无法做到的.并且它们也不支持Java泛型.但是Gson却将这两点作为自己非常重要的设计目标. 目标 使用toJs

基础知识漫谈(4):讲讲元数据

说几个风马牛不相及的词儿,spring的依赖注入定义,hibernate的数据映射定义,XML的DTD,再就是我们常说的报文格式. 如果对它们不甚了解,请参考章节一<想到哪儿写到哪儿>.有了基本的了解之后,应当隐约之中有一种感觉,“它们很相似”. 本篇文章要说的就是这个相似性,我管它叫做数据格式\元数据,DataSchema\MetaData.当然,元数据的定义是要大于数据格式的,本文将它们当成同一个概念. 什么是数据格式?看看这段XML(1): <Bean type=”com.nlo.

测试小白基础知识---常用的测试用例设计方法

软件测试的核心是测试用例的编写,是每个测试人员必须掌握的技能!! «««测试第一原则:所有的测试,都必须追溯到需求: «««测试第二原则:测试是无穷尽的,测试必须终止 «««测试用例的设计方法: 一.等价类划分法 某个输入域的子集合,在该子集合中,所有的输入数据对揭露软件中的错误都是等效的. 等价类划分有效等价类和无效等价类 有效等价类:输入的数据,是符合需求的,是合理的合法的. 无效等价类:输入的数据,是不符合需求的,是不合理的. «««等价类划分法用例设计原则: 1.划分有效和无效等价类,为

IPC网络高清摄像机基础知识3(Insta360硬件设计之路 “来自2015年”)

需求说明:Insta360硬件设计思路 来自:http://www.ifanr.com/568132 阅读精华整理: 1.刘靖康在大学就开始创业.在大二的时候他曾经去腾讯实习,然后又去了"超级课程表"这个 团队实习半年,2013 年 9 月回到南京创业,一开始的产品叫"名校直播",是一款围绕院校名 师讲座所做的视频直播产品.--既然是视频直播,就要非常讲究现场感. 2.然而,2014 年 5 月接受 IDG 100万美元的种子轮融资后,刘靖康和团队的主打产品从&qu

移动端尺寸基础知识

转:http://www.cnblogs.com/chris-oil/p/5367106.html 移动端尺寸基础知识 初涉移动端设计和开发的同学们,基本都会在尺寸问题上纠结好一阵子才能摸到头绪.我也花了很长时间才弄明白,感觉有必要写一篇足够通俗易懂的教程来帮助大家.从原理说起,理清关于尺寸的所有细节.由于是写给初学者的,所以不要嫌我啰嗦. 现象 首先说现象,大家都知道移动端设备屏幕尺寸非常多,碎片化严重.尤其是Android,你会听到很多种分辨率:480x800, 480x854, 540x9

系统架构之设计模式一、基础知识

从今天开始将常用到的设计模式整理.归档,首先介绍下设计模式基础知识. 一.设计模式分类 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 二.设计模式六大原则 1.开闭原则(Open Close Principle) 开