使用动画转换创建生动的用户体验
HTML 应用程序就是一个很好的例子:在某个页面中填写一些数据,单击提交(Submit)按钮,原页面将消除,取而代之的是另一个全新的用户界面。然后,您将仔细审视这个新界面,考虑自己应该做的事情并找到新的提交按钮。
如果应用程序在这些不同的应用程序状态之间创建一个更具逻辑性的流程,并为用户带来各个新用户界面(UI)会不会更好呢?如果用户不用重新打量各个新用户界面,而是采用应用程序的流程化方式会怎么样呢?
这正是动画转换的内容:使应用程序用户界面的转换具有动画效果,从而在这些状态之间创建一个无缝的流程。通过帮助用户理解用户界面之间的组合,转换可以帮助保证用户连接到程序。
当然,这个问题对开发人员而言意味着更多的工作。让应用程序消除一个界面并显示下一个界面是处理这种问题的最直观的方法。在界面之间运行某种动画通常要求理解动画过程,然后编写一大堆自定义代码使界面上的元素具有动画效果。
这正是编写 动画转换 库的原因:从根本上简化应用程序状态之间动画的流程,执行合理的默认动画,以及让您专注于编写应用程序代码而不是动画代码。
演示时间
我们来看一个简单的演示应用程序 FieldsOfText。该应用程序将模拟一个常见的功能(别处可能也有),用户可以请求 GUI 展开自己并提供更多文本字段。比如说,我将使用一个类似于此的对话框将 至关重要的卡通画 上传到 我的 java.net 博客。应用程序开始只有一个文本字段,但是通过单击 More 或者 Less 按钮可以增加或减少所显示的文本字段数量。
界面底部也有一个提交按钮,因此此类应用程序通常都有提交按钮。此处的按钮实际上并没有执行任何任务,不过它和其他用户界面元素的作用一样,都是显示动画转换的过程。
以下是用于显示 GUI 的代码:
// Add the More/Less buttons container
add(moreOrLess);
// Next, add the proper number of text fields
for (int i = 0; i < numFields; ++i) {
add(textFields[i]);
}
// Finally, add the Submit button at the bottom
add(submitPanel);
在这段代码中, moreOrLess 组件是一个面板,用于保存 More 和 Less 按钮。 textFields[] 数组用于保存将要填充到 GUI 中的各种不同的文本字段。而 numFields 表示我们希望此时显示的数量。 submitPanel 组件是一个面板,用于保存提交按钮。
下面是提供的用户的基本用户界面:
图 1. 应用程序的初始界面
当用户单击 More 按钮,原文本字段下面将增加一个文本字段,如下图所示:
图 2. 用户单击 More 按钮之后的应用程序界面
当用户单击 Less 按钮,最下面的文本字段将消失,用户界面将回到图 1。
在典型的应用程序中,上述步骤可能是先消除原始窗口然后再显示新的用户界面。与此不同,动画转换库将平稳地在这些状态之间切换。它将淡入或淡出将要消除或出现的组件(比如说一个文本字段),而这些组件可能位于不同的位置(如提交按钮)。举例说明,图 3 显示了在前两个界面之间转换时的用户界面:
图 3. 前两个界面之间的动画转换
在图 3 中,我们可以看到第二个文本字段的淡出效果,而提交按钮将向下移动。
如果要查看动态效果,可以下载我所创建的视频剪辑( QuickTime 或 MPEG-4 格式)查看转换效果。
接下来,我们将了解演示应用程序如何实现这种转换效果。
动画转换:API
从最基本的角度而言,这个库由一个 ScreenTransition 对象(负责建立和运行动画)和一个 TransitionTarget 接口(代码需要实现这个接口)组成。
ScreenTransition
建立动画的代码将使用参数创建这个对象。它的构造函数类似以下形式:
public void ScreenTransition(JComponent transitionContainer,
TransitionTarget target,
Animator animator)
此处:
transitionContainer 容器的子类将在转换过程中实现动画效果。
target 是 TransitionTarget 的一个实现,它定义了 setupNextScreen() 方法。该方法用于定义转换界面的GUI。
animator 定义实际动画的参数,比如说持续时间。关于 Animator 的完整描述不在本文讨论范围之内。有关更多信息,请参阅 参考资料 中的 Timing Gramework 参考指南。
要在应用程序中运行转换,可随时调用 start() 方法:
public void start()
调用此方法时, ScreenTransition 将建立运行转换所需的一切条件,然后将运行转换。
TransitionTarget
TransitionTarget 一是个简单的接口,它只含有一个方法:
public void setupNextScreen()
代码需要实现这个方法并建立转换容器 GUI。这样,当方法完成时,应用程序将处于转换完成时的状态。
API 的使用
接下来,我们来了解一下 FieldsOfText 应用程序是如何使用这个 API 的。
首先,我们建立了一个 Animator 运行后半程的转换动画。我们将在转换起始时刻添加一些加速和减速。这种加速/减速行为并不是必需的,但是一般而言非线性转换的动画效果会好一些( Smooth Moves 这篇文章有详细的解释)。
Animator animator = new Animator(500);
animator.setAcceleration(.2f);
animator.setDeceleration(.2f);
然后,我们建立 ScreenTransition 对象使用主(main) JComponent 对象作为转换的一个容器,以及 TransitionTarget 对象(它实现了 setupNextScreen() 接口)和之前建立的 animator:
ScreenTransition transition =
new ScreenTransition(this, this, animator);
当用户单击 More 或 Less 按钮时, actionPerformed() 方法将触发转换:
public void actionPerformed(ActionEvent ae) {
boolean changed = false;
if (ae.getSource().equals(moreButton)) {
if (numFields < MAX_FIELDS) {
numFields++;
changed = true;
}
} else if (ae.getSource().equals(lessButton)) {
if (numFields > 1) {
numFields--;
changed = true;
}
}
if (changed) {
transition.start();
}
}
最后,我们在 setupNextScreen() 方法处理来自 ScreenTransition 的回调:
public void setupNextScreen() {
// First, clear out the previous GUI and start from scratch
removeAll();
// Add the More/Less buttons
add(moreOrLess);
// Next, add the proper number of text fields
for (int i = 0; i < numFields; ++i) {
add(textFields[i]);
}
// Finally, add the Submit button at the bottom
add(submit);
}
注意,这段代码与之前的 GUI 建立代码几乎相同;惟一的不同之处就是首先调用了 removeAll() 消除以前的 GUI。这段代码的全部作用就是为应用程序的合适界面建立 GUI。
就是这样!应用程序还需要一些代码处理其他任务,比如说建立 JFrame、在背景中绘出蓝色的斜线条,以及建立与组件和布局管理有关的一些详细信息。但是应用程序的大部分实际逻辑,特别是与动画转换有关的代码都已经在上面列出了。
那么它是如何工作的呢?
工作原理
当您的代码调用 start() 方法时, ScreenTransition 将计算出转换容器中的组件的当前属性,比如说位置和大小。然后,应用程序将调用 setupNextScreen() 方法,用于建立下一个界面的 GUI。最后,应用程序将计算出刚才建立的界面中的组件的属性。
注意,系统期望转换容器在适当的时候共享组件,而不是使用完全不同的组件表示相同的事物。因此,如果转换前后的两个界面都有一个提交按钮,那么您可能希望在两种情况下都使用相同的 JButton 对象以获得期望的效果。否则,系统将认为前一个按钮消失而新的按钮出现取代它。这并没有特别的技巧;如果希望在界面之间共享组件,那么应该使用在界面中使用相同的组件。
在这种情况下, ScreenTransition 将含有两个界面中的组件的所有信息:哪个组件在哪个界面中、组件的位置、组件的大小等等。因此,我们便可以计算出两个界面中的组件将发生哪些变化。在界面之间发生变化的任何组件都将经历以下三种转换之一:
出现:第一个界面没有该组件,而第二个界面含有该组件。
消失:第一个界面含有该组件,而第二个界面没有该组件。
变化:两个界面都有该组件,但是组件将在两个界面之间出现改变。
然后, ScreenTransition 将为各个组件选择合适的转换效果:
出现:组件将在转换过程中淡入显示。
消失:组件将在转换过程中淡出显示。
变化:组件将在转换过程中移动或按比例缩放。
拥有所有这些信息之后, ScreenTransition 将开始运行动画,根据与各个组件相关的转换效果修改该组件的外观。
当转换完成时,控制将返回应用程序。同时,之前应用程序在 setupNextScreen() 中建立的 GUI 将处于活动状态。
自定义效果
应用程序将使用内建的转换效果处理一些常见的情况。通常,淡入淡出和移动/调整大小是组件的默认行为。但是,需要广泛使用动画转换的应用程序可能更希望为特定的组件或在特定的情况下创建自定义效果。比如说,< 您可能希望组件以拉近或拉远的方式消失,或滑出界面。
库中所编写的转换效果是可扩展的。因此,我们可以创建效果并在系统中注册该效果,然后在应用程序中插入自己的效果。这些效果与组件和转换类型相关(出现、消失或变化)。当 ScreenTransition 搜索合适的效果应用于特定的组件和转换类型时,它将使用自定义的效果(如果已注册)。
编写自定义效果其实相当简单:您只需重写两三个方法用于在转换过程中处理建立、消除和绘图之类的功能即可。有关如何编写自定义效果的内容不在本文的讨论范围之内。但是,如果对此特性感兴趣,可以阅读 这本书的网站 上的“第 18 章:动画转换”,其中展示了自定义效果的代码和实例。
小技巧
我们可以在动画转换中使用各种小技巧实现库所需的功能和性能。关于这些技巧的完整介绍,请参阅代码和图书获得详细信息。比如说,动画转换库在转换完成前处理第二个界面的方式就很有趣。还有,动画转换库处理两个界面中的布局的方式也值得一看。同时,动画转换库还可以在转换过程中快速显示组件的图像快照,从而支持更佳的性能和复杂动画。
未来的工作
动画转换库这个项目还在发展之中。我希望负责该项目的开发人员可以继续重新定义其 API 并增强其功能。与此同时,动画转换库的实用性是毋庸质疑的。它应该让开发人员能够方便且无缝地连接到不同的应用程序状态,并为他们的用户提供生动的用户体验。
尝试使用动画转换库并将意见发送给我。