java

位置:IT落伍者 >> java >> 浏览文章

轻松实现Java用户界面编程


发布日期:2024年03月14日
 
轻松实现Java用户界面编程

Buoy 是一个构建在 Swing 之上的免费用户界面(UI)工具包它为 UI 开发人员提供了方便性和简单性在本文中作者用一个简单的 fractal 用户界面程序介绍了 Buoy 可以做什么为什么这么做

第一次尝试用 Java 语言构建简单的用户界面时我对 Swing 接口的复杂性感到有些惊讶老实说有点想打退堂鼓最近一个朋友向我提到他使用的渲染程序 Art of Illusion(请参阅 参考资料)基于一个不同的工具包Buoy推荐它的原因之一是它的界面更友好当他第一次提到它时我以为他在谈 BUI而它与 GUI 这个名字的相似是故意的在这里 B 代表 better(更好)但是名字 Buoy 并不是缩写

Buoy 是免费的实际上它是公共的东西它并没有在某个开放程度合理的许可下发布实际上它根本不受任何许可控制这意味着在任何用 Java 语言编写的能够运行 Buoy 的项目中都可以使用 Buoy而不用考虑许可问题因为提供了完整的源代码所以这个工具包很容易修改和扩展本文基于 Buoy 发行版要求读者对 Swing 有基本的了解虽然不了解也能对付过去

示例程序

我曾经尝试用 Swing 构建的第一个应用程序最后以失败告终为了看出工具包之间的对比情况我决定使用 Buoy 来构建这同一个程序文章中的代码示例全部来自该程序的 Buoy 版本程序生成了一些分形具体地说是迭代的分形基本思想很简单在平面上定义一系列的线条区段从() 到()围绕任意一个单位线条定位绘制这些区段之后绘制同一套变形线条用这个区段作为单位向量做起来比说的更容易就像在图 中看到的

分形编辑器中的分形

这个程序的界面相当简单它有一些界面小部件有一个画布在画布上绘制漂亮的图片还支持用鼠标操纵图片实际上必须要做的全部工作就是操纵构成原始曲线的点原始曲线会迭代地绘制出来界面还有一个最小化的菜单;它可以打开和关闭文件关闭窗口或者把当前图像保存为 PNG 格式的文件虽然简单但是这个界面简要地提供了一个 Buoy 小部件的合理示例还有相当数量对事件处理系统的体验

程序实际的核心代码 —— 分形生成器 —— 已经写好了这把这个示例变成一个很好的测试程序当然在更新它的过程中我也发现并且修补了一些 bug

发行包中包含示例程序的源代码还有编译好的类文件和 Buoy 的 JAR 文件(单击本文顶部或底部的 Code 图标下载 factaltar)包中还包含一个叫做 frac 的目录里面包含一些示例分形如果使用一台 UNIX 风格的机器在路径中有 Java 编译器那么只要运行 make 就能运行它否则需要设置 classpath 包含当前路径和 Buoy 的 JAR 文件所在的目录然后运行 FractalViewer 类在 Windows 系统上正确的命令行应当是 java classpath ;Buoyjar FractalViewer

sed e s/J/B/g

在第一次深入研究代码时也许会形成这样的印象把 Swing 代码转换成 Buoy 代码简单得就像把 UI 元素名称中的字母 J 换成 B 一样简单例如 FractalViewer 类不再扩展 JFrame;它现在扩展的是 BFrame主要的小部件名称也可以照此推测得到Spinner 和 slider 像以前一样有相同的名字只是换了一个字母 MenuBar(菜单条) 仍然由 Menus(菜单)构成菜单则容纳 MenuItems

有些命名转换略有不同在 Swing 引用 BorderLayout 的地方Buoy 有 BorderContainer一般来说Buoy 的命名转换相当统一虽然不总是与 Swing 的命名一样一个明显的区别是 Buoy 几乎组合了容器和布局管理器的概念;每种容器类型都知道自己如何布局这大大简化了设计例如在分形生成器中使用的 LabelWidget 类是一个 BorderContainer;在 Swing 中这可能是一个带有 BorderLayout 布局管理器的 JPanel

但是两者还是有许多相似之处这对适应新东西有很大帮助更重要的是Buoy 构建在 Swing 之上这意味着一般来说如果需要做的事不能轻松地用 Bouy 完成时可以把 Buoy 对象传递给它包装的 Swing 对象对于这种情况如果想访问一些没有 Buoy 对应物的 Swing 对象可以简单地把它包装在 AWTWidget 对象中这个对象提供了非常薄的包装器通过它不仅 Buoy 自己的小部件而且所有的小部件都能访问 Buoy 的小部件 API例如如果发现确实需要 GridBagLayout可能就需要这样做

例如FractalPanel 类是一个 AWTWidget在早期设计中 它是 JPanel 的子类 但实际上我并不需要 JPanel 代码相反我构建了包装定制类的类 FractalCanvas 它本身是普通的 Canvas 类的一个子类把它变成一个 AWTWidget就可以在它上面利用 Buoy 高效的事件处理机制

事件处理代码非常简单在按下鼠标按钮时通过 addEventLink() 的魔力Buoy 发送一个新的 MousePressedEvent 事件到 mousePressed() 函数我忽略了按下哪个按钮这个问题只考虑按住 shift 单击或普通单击普通单击选择最靠近的点而按住 shift 单击则重新把显示居中然后如果鼠标移动那么每次 Buoy 注意到移动时都会开始发送 MouseDraggedEvent 事件在处理这些事件时FractalPanel 会生成自己的事件

近观 PointChangedEvent

为了让一些讨论更加具体请来看 PointChangedEvent这是一个试验性的类如果不喜欢它那也只能怪老天了这个类的想法是让一个类来表示状态点中的变化编辑器跟蹤当前点 —— 也就是编辑器小部件目前正在编辑的点可以用这些小部件或在分形面板中单击选择新的点选择的是最靠近的点

我得出这样一个结论在代码中大概有三类涉及到点的事件需要从一个类发送到另一个类

一个是改变某个点的特征 POINT 事件类型如果由编辑器发送就是告诉分形改变原型线条上的点并要求重画线条如果由分形发送则是告诉编辑器刚刚选中的点的特性

下一个是选择某个点可以按索引或位置进行选择所以如果只提供了索引或位置那么构造函数会认为意图是填充其他值有一点特殊的地方点索引 用来表示没有选中的点所以必须用 表示编辑器正在寻找指定位置的点这可能不漂亮但是有效

有点意思的是 Fractal 类响应 SELECT 事件的方式如果成功地选择了一个点就会发回一个新的 POINT 类型的 PointChangedEvent 事件如清单 所示

清单 用事件回答事件

case PointChangedEventSELECT:

if (egetIndex() >= )

selectPoint(egetIndex());

else

selectPoint(egetPoint());

// just in case they dont know

event(new FractalChangedEvent(FractalChangedEventSIZE size));

if (selectedPoint >= && selectedPoint < size)

event(new PointChangedEvent(selectedPoint points[selectedPoint]));

else

event(new PointChangedEvent(selectedPoint null));

event(new FractalChangedEvent(FractalChangedEventREDRAW));

break;

最后移动点是一个特殊情况如果不需要改变点的其他属性(例如颜色)那么所要处理的就是位置这就是 MOVE 事件类型在效果上它与 POINT 事件类型效果很像但它不需要事件生成器(通常是 FractalPanel 类)去关心那些它根本不知道的属性

INSERT 和 DELETE 事件类型只有部分相关可能应当属于 FractalChangedEvent 事件

事件处理

正如已经开始看到的事件处理是 Buoy 与 Swing 最明显的不同之处事件处理提供了大量灵活性Buoy 本身的事件集相当丰富且允许您挑选自己感兴趣的事件从任何小部件向其他对象发送事件例如如果想在 Swing 中捕获鼠标事件捕获事件的类需要实现 MouseListener 接口这个接口有 个函数需要实现即使它们就是摆设也必须实现而且必须使用接口提供的函数名称更糟的是函数必须是侦听器接口的公共部分;要么把这作为公共接口的一部分公开要么创建一个什么都不做只是包装事件侦听器代码的内部类

在 Buoy 中每个小部件都是 EventSource 这意味着可以从每个小部件侦听事件什么类型的事件呢?任何类型都可以关键的函数是 addEventLink()这允许您指定类侦听器以及可选的方法每当 EventSource 分派这个类或它的子类的事件时侦听器都会接收到事件要么是通过一个叫做 processEvent()的方法要么是通过在开始调用 addEventLink() 时提供的方法名称提供的函数不能接受参数也不能接受与指定事件类型兼容的类的对象;父类和接口可以

这是一个方便的设置可以把不同的事件路由到不同的函数或相同的函数例如MousePressedEvent 和 MouseReleasedEvent 会被分别处理在示例程序中鼠标的按下释放和拖动分别有不同的线程如清单 所示注意这远远超过 Swing 的 MouseListener 所能做的如果用 Swing 编程的话就需要实现 MouseListener 和 MouseMotionListener 这两个接口

清单 只挑感兴趣的事件

thisaddEventLink(MousePressedEventclass this mousePressed);

thisaddEventLink(MouseRele

               

上一篇:浅析Java内部类在GUI设计中的作用(2)

下一篇:防止同一个Java应用重复启动的shell脚本