`

基于Java的界面布局DSL的设计与实现

    博客分类:
  • Java
阅读更多

  Java界面设计应该是一项充满创造性、富有乐趣的工作,但是却往往被认为非常的枯燥和繁琐。究其原因,是因为界面布局领域所采用的描述概念和具体的实现语言之间存在很大的语义隔阂。而一般的界面开发工具提供的所见即所得以及界面布局管理器等方案也无法很好地解决这个问题。

  在本文中,我们会给出一种更好的解决方案,我们不是去试图把界面设计者头脑中的设计概念和样式逐步降级、分解成所使用的实现语言能够理解的低层概念,也不是提供一些已经完成的、确定的但难以扩充和更改的布局样式库供界面设计者使用。我们所提供的是一种专门用于描述高层界面设计样式的语言。通过这种语言,界面设计者可以直接、明确地描述出他们头脑中的布局设计样式;通过这种语言,界面设计者可以自己方便地、灵活地制定自己需要的布局样式。此外,本文中给出的设计思想对于其他领域的设计也有很好的借鉴作用。

  创造性,还是乏味?

  界面设计是一项非常有创造性,甚至富有艺术性的工作,一个简洁、易用、漂亮的界面在带给使用者方便的同时,也会给界面设计者带来极大的成就感。但是,在现实中,情况似乎并非如此,很多人都认为做界面是一项非常繁琐、机械、乏味的工作,并千方百计地去逃避界面相关的工作。这是为什么呢?

  原因很简单,因为做界面其实涉及两项工作,一项是界面的一些设计创意,包括界面的布局样式以及和使用者的交互方式,这项工作充满挑战和乐趣。但是,这些设计创意最终是要落实到实现上的,这就是第二项工作。此时,你头脑中那些清晰、完整的设计概念开始变得琐碎,你不得不和那些低层次的坐标位置打交道。更糟糕的是,当你好不容易做好了一个界面,但是发现其中某些元素的布局需要一些调整时,这个你本应认为是一个很简单的改变却造成大量重复的低层次坐标位置更改时,你肯定会认为做界面是多么的机械和乏味呀!

  其实,造成这种认识的根源在于界面设计创意和实现这些创意概念的语言之间存在很大的断层。这样,在具体实现时,你就必须得把这些清晰、完整的布局样式降级成一些琐碎、没有什么意义的低层次的坐标值,使得实现语言能够理解。这项工作不仅乏味,而且最终的实现也非常的脆弱 —— 一个在布局样式层面非常简单的更改,就会造成实现层面的巨大变动。比如:我们可以说把一组元素同时按比例缩小 10%,做过界面的朋友肯定知道这个更改意味着什么。

  为了应对这个断层的问题,目前几乎所有的涉及界面制作的开发工具都提供了相同的解决方法:可视化的界面设计工具以及布局管理器。但是这两种方法都没有从根本上解决这个问题。

  可视化界面设计工具确实避免了不少繁琐的界面元素摆放工作,但是对于专业的界面设计来说,通过拖放设计出来的界面在准确度和规范性上都有待提高,此外还有更为重要的一点,那就是存在于设计者头脑中的布局样式仍然没有被明确地描述出来,而是被降级成一个个摆放在一起的零散的组件,虽然这些组件本身是可视的。这个语义断层的存在同样会使得通过可视化界面设计工具设计出来的界面非常脆弱。

  布局管理器试图通过提供一些常用的布局样式来解决这个问题。但是,这种做法非常僵化,也就是说你只能使用现有的布局管理器,如果它们无法满足你的要求,你也无法自己定制。此外,这些布局管理器仅仅适合于一些简单的情况。对于一些复杂的布局样式来说,它们的描述能力就显得非常的不足。那些曾经和 GridBagLayOut 斗争过的朋友对此肯定深有体会。

  在本文中,我们会给出一种更好的解决方案,我们不是去试图把界面设计者头脑中的设计概念和样式逐步降级、分解成所使用的实现语言能够理解的低层概念,也不是提供一些已经完成的、确定的但难以扩充和更改的布局样式库供界面设计者使用。我们所提供的是一种专门用于描述高层界面设计样式的语言。通过这种语言,界面设计者可以直接、明确地描述出他们头脑中的布局设计样式,通过这种语言,界面设计者可以自己方便地、灵活地制定自己需要的布局样式。也就是说,本来仅存在于界面设计者头脑中的抽象布局样式,现在也变得可描述,可编程了。

  界面布局语言介绍

  在学习界面布局语言的设计之前,先来了解一下该语言的使用是非常有帮助的。我们的界面布局语言非常简单,简单到只有一种原子:Component。Component 是一种基本的布局元素,可以对 Component 进行平移和伸缩,使其和给定的一个布局空间 Rectangle 匹配。比如对于 Button 这个 Component 来讲,它具有传统按钮的外观,但是它在布局上所占的实际空间则是由为其指定的 Rectangle 决定的。此外,Component 要最终在界面上显示出来,就必须有一个物理上的 Container。也就是说,界面设计应该是一项充满创造性、富有乐趣的工作,但是却往往被认为非常的枯燥和繁琐。究其原因,是因为界面布局领域所采用的描述概念和具体的实现语言之间存在很大的语义隔阂。而一般的界面开发工具提供的所见即所得以及界面布局管理器等方案也无法很好地解决这个问题。

  在本文中,我们会给出一种更好的解决方案,我们不是去试图把界面设计者头脑中的设计概念和样式逐步降级、分解成所使用的实现语言能够理解的低层概念,也不是提供一些已经完成的、确定的但难以扩充和更改的布局样式库供界面设计者使用。我们所提供的是一种专门用于描述高层界面设计样式的语言。通过这种语言,界面设计者可以直接、明确地描述出他们头脑中的布局设计样式;通过这种语言,界面设计者可以自己方便地、灵活地制定自己需要的布局样式。此外,本文中给出的设计思想对于其他领域的设计也有很好的借鉴作用。

  创造性,还是乏味?

  界面设计是一项非常有创造性,甚至富有艺术性的工作,一个简洁、易用、漂亮的界面在带给使用者方便的同时,也会给界面设计者带来极大的成就感。但是,在现实中,情况似乎并非如此,很多人都认为做界面是一项非常繁琐、机械、乏味的工作,并千方百计地去逃避界面相关的工作。这是为什么呢?

  原因很简单,因为做界面其实涉及两项工作,一项是界面的一些设计创意,包括界面的布局样式以及和使用者的交互方式,这项工作充满挑战和乐趣。但是,这些设计创意最终是要落实到实现上的,这就是第二项工作。此时,你头脑中那些清晰、完整的设计概念开始变得琐碎,你不得不和那些低层次的坐标位置打交道。更糟糕的是,当你好不容易做好了一个界面,但是发现其中某些元素的布局需要一些调整时,这个你本应认为是一个很简单的改变却造成大量重复的低层次坐标位置更改时,你肯定会认为做界面是多么的机械和乏味呀!

  其实,造成这种认识的根源在于界面设计创意和实现这些创意概念的语言之间存在很大的断层。这样,在具体实现时,你就必须得把这些清晰、完整的布局样式降级成一些琐碎、没有什么意义的低层次的坐标值,使得实现语言能够理解。这项工作不仅乏味,而且最终的实现也非常的脆弱 —— 一个在布局样式层面非常简单的更改,就会造成实现层面的巨大变动。比如:我们可以说把一组元素同时按比例缩小 10%,做过界面的朋友肯定知道这个更改意味着什么。

  为了应对这个断层的问题,目前几乎所有的涉及界面制作的开发工具都提供了相同的解决方法:可视化的界面设计工具以及布局管理器。但是这两种方法都没有从根本上解决这个问题。

  可视化界面设计工具确实避免了不少繁琐的界面元素摆放工作,但是对于专业的界面设计来说,通过拖放设计出来的界面在准确度和规范性上都有待提高,此外还有更为重要的一点,那就是存在于设计者头脑中的布局样式仍然没有被明确地描述出来,而是被降级成一个个摆放在一起的零散的组件,虽然这些组件本身是可视的。这个语义断层的存在同样会使得通过可视化界面设计工具设计出来的界面非常脆弱。

  布局管理器试图通过提供一些常用的布局样式来解决这个问题。但是,这种做法非常僵化,也就是说你只能使用现有的布局管理器,如果它们无法满足你的要求,你也无法自己定制。此外,这些布局管理器仅仅适合于一些简单的情况。对于一些复杂的布局只要给定了一个 Rectangle 和一个 Container,一个 Component 就可以在界面上指定的布局位置呈现出来。

  例如,当我们使用布局语言在一个 JFrame 上坐标位置为 (0,0) 展示一个 width 为 200,height 为 60 的按钮时,我们可以这样来描述(为了简洁起见,后面的代码实例中均略去 Layout 名字空间前缀):

Button().title(“button1”).at(0,0,200,60).in(this.getContentPane());

  仅仅提供这样一种原子元素的语言显然无法满足我们前面提到的目标。在我们的界面布局语言中,还提供了两种在布局中非常常用的两种从已有组件构造新组件的组合手段:above 和 beside。其中 above 组合子接收 3 个参数:两个现有 Component 以及一个比例,它会产生出一个新的复合 Component,其中按照给定的比例把第一个 Component 摆放在第二个 Component 之上。Beside 组合子接收同样的 3 个参数,并且也产生出一个新的复合 Component,其中按照给定的比例把第一个 Component 摆放在第二个Component左边。

  例如,如果我们希望在一个给定的 Container C 上的 Rectangle(0,0,300,40) 中,平行摆放一个 TextField 和一个 Button,且希望 TextField 占据 80% 的比例时,可以这样来描述:

beside(TextField(), Button().title(“ok”), 0.8).at(0,0,300,40).in(C)

  同样,我们可以使用 above 来进行如下描述:

above(TextField(), Button().title(“ok”), 0.5).at(0,0,300,60).in(C)

  值得注意的是,在我们的界面布局语言中,Component 在 beside 和 above 操作下是封闭的,也就是说 beside 和 above 操作的结果同样也是 Component,并完全可以作为基本的 Component 来再次进行 beside 和 above 组合。这样我们就可以使用这两个简单的操作生成更加复杂的 Component 来,从而完成复杂的界面布局。比如,我们可以这样来进行描述:

Component L = beside(TextField (), Button().title(“…”), 0.8); 
above(L, Button().title(“ok”), 0.5). at(0,0,300,60).in(C)

  为了保证界面布局语言的完备性,我们增加了一种特殊的原子元素:Empty。它的作用只是占据一定的布局空间。比如,如果我们希望在一个布局空间中右半边放置一个 Button,左半边空置,就可以这些描述:

beside(Empty(), Button(), 0.5).at(0,0,200,40).in(C)

  读者在后面可以看到,正是这个 Empty 以及 beside 和 above 操作的闭包性质为我们描述任意复杂的布局样式提供了可能。

  在有了这些基础的布局元素和组合手段后,我们就可以通过组合手段来把一些典型的布局样式抽象出来。在下一小节中读者将会看到,布局语言中的 beside 和 above 组合操作其实就是 Java 中的普通方法,因此我们的布局语言中不需要什么特别的抽象手段。也就是说,我们可以直接使用 Java 中已有的抽象手段。

  例如,如果我们希望抽象出这样一种布局样式,其中给定一个布局空间和一个布局组件,我们期望该组件能够按照指定的纵、横留白比例位于该布局空间的中心地带。我们可以把该布局样式抽象出来,并命名它为 center。并可以在更复杂的布局样式中把 center 当作一个基本语素使用。center 的实现如下:

public Component center(Component cp, float hRatio, float vRatio) {
float s1 = (1-2.0* hRatio)/ (1.0 - hRatio); 
float s2 = (1-2.0*vRatio)/ (1.0-vRatio); 
Component u = above(Empty(), above(cp, Empty(), s2), vRatio); 
return beside(Empty(), beside(u,Empty(), s1), hRatio); 
}

  当我们想把一个按钮放置按照在横向 0.2,纵向 0.1 的留白比例放在布局空间 (0,0,100,30) 中时,我们可以简单的进行如下描述:

center(Button().title(“I am at center.”), 0.1,0.1).at(0,0,300,60).in(C)

  我们还可以构建出 h_seq 和 v_seq 这样的布局样式,它们分别为把一组给定的布局元素横向顺序排列和纵向顺序排列,其实现如下:

public Component h_seq(Component[] cps) {
int len = cps.length; 
if(len == 1) return cps[0]; 
return beside(cps[0], h_seq(slice(cps, 1, len)), 1.0/len); 
}

public Component v_seq(Component[] cps) {
int len = cps.length; 
if(len == 1) return cps[0]; 
return above(cps[0], v_seq(slice(cps, 1, len)), 1.0/len); 
}

  其中 slice 方法有 3 个参数,一个为布局元素数组,另外两个为区间的起止位置,该方法把给定布局元素数组中指定起止位置的区间部分作为一个新的布局元素数组返回。这两个方法的实现都比较简单直接。下面是两个应用例子:

Component[] cps = new Component[]
{ Button().title(“1”), Button().title(“2”), Button().title(“3”) }; 
h_seq(cps).at(0,0,300,60).in(C)
v_seq(cps).at(0,0,150,200).in(C)

  在 center、h_seq、v_seq 这些布局样式的基础上,我们可以定义出更加高阶的样式来,比如,给定一布局元素序列,我们希望它们在给定的布局空间中按照 N 行、M 列排列。我们称之为 block,其实现如下:

public Component block (Component[] cps, int N, int M) {
Component[][] fcps = formalize(cps, N, M); 
Component[] rows = new Component[fcps.length]; 
for(int i = 0; i < fcps.length; i++) {
rows[i] = h_seq(fcps[i]); 
}
return v_seq(rows); 
}

  其中 formalize 是一个工具方法,它把一个给定的布局元素数组规范化为 N 行 M 列的形式,如果不足则用 Empty 组件补齐。

  如果希望在 block 中,每个元素都可以指定一些横向和纵向的留白,则可以定义一个 block_with_margin 布局样式,其实现如下:

public Component block_with_margin(Component[] cps, int N, int M, 
float hRatio, float vRatio) {
Component[] ncps = new Component[cps.length]; 
for(int i=0; i<cps.length; i++) {
ncps[i] = center(cps[i], hRatio, vRatio); 
}
return block(ncps, N, M); 
}

  好了,现在我们来看一个稍微复杂一些的例子,我们将使用前面制作的一些布局样式构建一个迷你计算器的外观,如下图所示:

  对应的描述代码如下:

Component[] cs = new Component[]{
Button().title("0"), 
Button().title("1"), 
Button().title("2"), 
Button().title("+"),
Button().title("3"), 
Button().title("4"), 
Button().title("5"), 
Button().title("-"),
Button().title("6"), 
Button().title("7"), 
Button().title("8"), 
Button().title("*"),
Button().title("9"), 
Button().title("="), 
Button().title("%"), 
Button().title("/") 
}; 
Component opLayout = block(cs,4,4); 
above( above( TextField(),
beside( Button().title("Backspace"), Button().title("C"),0.5), 0.5), 
block(cs,4,4), 0.3).at(0,0,300,200).in(C);

  如果我们现在希望将所有数字以及操作按钮按照横向和纵向各 2% 进行留白,我们所要做的仅仅是一行的改动,就是把:

Component opLayout = block(cs,4,4);

  更改为:

Component opLayout = block_with_margin(cs, 4, 4, 0.02, 0.02);

  这意味着什么呢?这意味着我们可以直接使用布局语言进行界面制作,我们可以直接针对布局进行编程,我们所写出来的界面代码就是我们的布局规格说明。

  从上面的介绍中,读者可以看出,我们的界面布局语言可以非常方便地定义出一些常见的布局样式,还可以把这些样式组合成更为复杂的一些高阶布局样式,并且这种组合是没有任何限制的。此外,这些布局样式的定义描述方式是和界面设计者头脑中所使用的一些布局词汇和规则贴近的。通过使用界面布局语言,界面设计者完全可以摆脱那些呆板、机械又难以定制和扩展的布局管理器,可以轻松地把头脑中的布局创意直接描述出来,逐步形成自己的布局样式库,充分享受这种创造性的工作所带来的乐趣。

分享到:
评论

相关推荐

    利用 Java 实现组合式解析器,基于 Java 的界面布局 DSL 的设计与实现(转载)

    4. **DSL 设计**:如何定义一个简洁、直观的界面布局DSL,包括关键字选择、语法规则制定等。 5. **Java 内部 DSL**:如何利用 Java 的类、接口和方法来构造内部DSL,让代码更易读、易维护。 6. **解析库的使用**:...

    基于Java的订餐系统安卓设计源码

    本项目是一个基于Java技术开发的安卓订餐系统源码,主要功能是为餐饮行业提供一个便捷的在线订餐和订单管理平台。以下是该系统的一些关键知识点: 1. **Java编程语言**:整个系统的核心编程语言是Java,这是一种...

    JAVA上百实例源码以及开源项目源代码

    例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在...

    Android-一个简洁的汇率转换软件使用kotlin编写

    本项目是一个基于Kotlin的简洁汇率转换应用,它利用了Anko库来简化UI创建,并实现了主题的动态切换效果,为用户提供更优质的交互体验。 首先,我们来了解Kotlin的基础知识。Kotlin是JetBrains公司开发的一种静态...

    groovy-jmx-dsl:用于基于 Swing 的 JMX 仪表板的 Groovy DSL

    2. 示例:可能包含了一些预设的 Groovy 脚本,展示如何使用 DSL 来定义界面布局和连接到 JMX 服务器。 3. 文档:可能有项目文档,解释如何安装、配置和使用 Groovy-JMX-DSL。 4. 测试:可能包含了测试用例,帮助验证...

    java学习笔记4

    9. **JavaFX或Swing**:如果涉及到图形用户界面(GUI)开发,那么JavaFX或Swing可能是讨论的内容,包括组件的使用、布局管理、事件处理等。 10. **Spring框架**:如果笔记内容深入到企业级开发,那么Spring框架的...

    Android-用kotlin实现的纯粹倒计时应用

    本项目是一个基于Kotlin实现的纯粹倒计时应用,它提供了基础的倒计时功能,帮助用户跟踪时间,比如用于设定目标、提醒事件或者简单的计时任务。以下是关于这个项目的一些关键知识点和实现细节: **1. Kotlin基础** ...

    dsl-maker:Kotlin多平台库,可帮助使用ANTLR或简单的内置解析器创建DSL

    - **UI描述**:定义用户界面布局,使得非程序员也能参与到UI设计中。 - **测试脚本**:创建简洁的测试语言,方便编写测试用例。 在“dsl-maker-main”这个压缩包中,很可能是包含了DSL-maker库的源码,通过查看和...

    Kotlin的轻量级JavaFX框架

    在Java开发领域,GUI图形用户界面的设计与实现是不可或缺的一部分。JavaFX提供了一种强大的工具集,用于创建桌面应用的富客户端界面。而当Kotlin与JavaFX结合时,我们可以得到一个更加简洁、易读且高效的开发体验。...

    无线智能表数据采集与管理系统(客户端)软件设计.zip

    在UI设计中,Kotlin的Anko库提供了简化XML布局的DSL(领域特定语言),使开发者能更直观地构建用户界面。此外,Kotlin的可空性检查和扩展函数特性,有助于创建更加健壮的UI组件。 数据处理是系统的核心部分。在无线...

    2022年广州大学拟定题目-202111201

    【基于Java Web的在线考试系统】 Java Web技术用于构建分布式、跨平台的网络应用,本课题旨在构建一个在线考试系统,包含如下功能: 1. 学生模块:参加考试、修改个人信息、查看成绩。 2. 教师模块:维护试题、...

    Grails_Portal.pdf

    - 设计和实现门户布局及功能。 - 集成外部服务和数据源。 - 测试和部署门户应用。 #### Grails Portal未来展望 - **发展方向**:Grails Portal框架未来的发展方向可能包括增强安全性、提高性能、增加更多高级...

    自己编写的100个javacc例子程序

    `GUIParsing`可能涉及到解析与图形用户界面相关的语法,比如XML布局文件。`JJTreeExamples`则可能是一系列使用JavaCC的扩展工具JJTree创建的语法树示例,JJTree可以将解析树转换为抽象语法树(AST),方便进行进一步...

    android-java

    3. **布局**:XML文件用于定义用户界面的布局,但Java代码可以动态地创建和修改布局。例如,你可以根据用户行为或数据状态动态添加或移除View。 4. **数据存储**:Android提供多种数据存储方式,如SQLite数据库、...

    GEF原理的详细介绍+程序实例和程序源码

    EMF基于Java对象模型,可以生成模型的XML序列化、持久化支持以及相应的Java类,使得数据模型能够方便地被读取、存储和交换。 **GEF原理** 主要包括以下几个关键部分: 1. **模型(Model)**:这是GEF应用程序的基础...

    Android项目影视评价项目文档

    2. `java`目录存放Java源代码和测试代码,是实现业务逻辑的核心部分。 3. `res`资源目录则包含了各种项目资源: - `drawable`用于存储图片资源。 - `layout`存放XML布局文件,定义用户界面。`activity_main.xml`...

    eclipse-scout-2023-09-R-win32-x86-64.zip

    Eclipse Scout 是一个开源的企业级应用开发框架,主要用于构建基于Java和HTML5的桌面和Web应用程序。这个压缩包“eclipse-scout-2023-09-R-win32-x86-64.zip”包含了针对Windows 64位系统的Eclipse Scout的最新版本...

    android-studio-ide-202.7486908-mac

    4. **布局设计器**: 提供视觉化的界面布局编辑器,可实时预览UI在不同设备上的效果,并支持拖放组件,简化UI设计。 5. **调试工具**: 内置强大的调试器,支持单步执行、设置断点、查看变量值、性能分析等功能,帮助...

Global site tag (gtag.js) - Google Analytics