好久没来这里更新了,转一个我在我的msn space上面写的blog:
今天在网上看到了一个很有趣的关于 Swing 扩展的小例子,觉得很有意思,我把他的代码稍微作了些改进,需要的朋友可以立刻在项目中使用。
这个例子主要是为了实现一个“震动”的对话框,这种震动功能在苹果机上应用的比较多。原文对源代码进行了很详细的解释,希望英文好的朋友直接去看原文,然后再回来继续看我的文章,原文的连接附在文章的最后。
下面的代码已经被我做了适当的修改,在代码中, 我也添加了适当的注释,
public class DialogEarthquakeCenter extends Object {
// 窗口距离中心左右晃动的最大距离
public static final int SHAKE_DISTANCE = 10;
// 窗口晃动一个循环(中间,右,中间,左, 中间)所需要的时间 (ms),
// 这个值越小, 晃动的就越快。
public static final double SHAKE_CYCLE = 80;
// 整个晃动所需要的时间。
public static final int SHAKE_DURATION = 300;
// 这个是设定 Swing 多长时间( ms )更新窗口的位置。
public static final int SHAKE_UPDATE = 5;
private JDialog dialog;
private Point naturalLocation;
private long startTime;
private Timer shakeTimer;
private final double HALF_PI = Math.PI / 2.0;
private final double TWO_PI = Math.PI * 2.0;
public DialogEarthquakeCenter (JDialog d) {
dialog = d;
}
public void startShake() {
// 保存窗口的原始位置
naturalLocation = dialog.getLocation();
// 保存开始的时间
startTime = System.currentTimeMillis();
shakeTimer =
new Timer(SHAKE_UPDATE,
new ActionListener() {
public void actionPerformed (ActionEvent e) {
// 计算倒目前为止花费的时间
long elapsed = System.currentTimeMillis() -
startTime;
// 下了三角公式是为了利用时间计算出某一时刻晃动的幅度,举例见 A.
double waveOffset = (elapsed % SHAKE_CYCLE) /
SHAKE_CYCLE;
double angle = waveOffset * TWO_PI;
double angley = waveOffset * TWO_PI;
int shakenX = (int) ((Math.sin(angle) *
SHAKE_DISTANCE) +
naturalLocation.x);
int shakenY = (int) ((Math.sin(angley) *
SHAKE_DISTANCE) +
naturalLocation.y);
dialog.setLocation (shakenX, shakenY);
dialog.repaint();
// should we stop timer?
if (elapsed >= SHAKE_DURATION)
stopShake();
}
}
);
shakeTimer.start();
}
public void stopShake() {
shakeTimer.stop();
dialog.setLocation (naturalLocation);
dialog.repaint();
}
// 去掉了 原始代码中的 main
}
A. 下面我来解释一下这里用到的三角函数,其实非常简单,其目的就是让窗口流畅的晃动。
double waveOffset = (elapsed % SHAKE_CYCLE) /SHAKE_CYCLE;
由于窗口是在相对于他的原始位置做周期震动,这个公式是为了将震动的时间换算成相对震动一周的相对比例,举例来说就是:( % 是取余运算)
20ms 时,震动了 1/4 周, waveOffset = ( 20%80 ) /80 = 0.25, 到右边最远处。
40ms 时,震动了半周, waveOffset = ( 40%80 ) /80 = 0.5, 回到起点位置。
60ms 时,震动了 3/4 周, waveOffset = ( 60%80 ) /80 = 0.25, 到左边最远处。
80ms 时,整好震动一周, waveOffset = ( 80 %80 ) /80 = 0 , 回到起点位置。
然后继续循环
double angle = waveOffset * TWO_PI
将上述介于 0 到 1 的比例换算成角度,也就是 20ms-1/4 周 -PI/2 。
int shakenX = (int) ((Math.sin(angle) *
SHAKE_DISTANCE) +
naturalLocation.x);
然后再将角度利用 sin 函数换算成相对应的震动幅度。到这里可能会有朋友要问,问什么不利用时间比例 waveOffset 直接换算成震动幅度, 就是好象这样:
int shakenX = (int) ((waveOffset*
SHAKE_DISTANCE) +
naturalLocation.x);
这样也是可以的,利用三角函数可以使振动更加圆滑更加真实,因为 y = sin ( x )函数波浪形的曲线,结果是靠近中心的地方震动幅度变化的比较快,远离中心的地方震动幅度变化的比较慢。而使用 waveOffset 直接换算成震动幅度使用的是 y=ax 函数,他的函数图是直线,导致震动幅度变化比较生硬。
原始的代码里面只是设置了 x 方向的震动,我又加入了 y 方向的震动,并且调整了一些参数,使得震动比较柔和。 还有就是,原始代码是含有 main 方法的 application 。在实际开发中根本没用,我把她改写成了一个简单的类。在实际开发中只要在 DialogFactory 里面增加一个方法生成这种对话框就可以了,编码人员可以轻松使用, i.e. 。 DialogFacotry.createShakeMessageDialog(……)
Public final class DialogFactory {
……
// 这里使用 enum 是为了实现 I18N ,如果不需要的话,可以直接使用 String
private void create ShakeMessageDialog(Enum title,
Enum message) {
// 作为例子,这里只是生成一个简单的信息框,只带有一个 ok 按钮。
JOptionPane pane = new JOptionPane(message.localized(),
JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION,
null, null, null);
JDialog dialog = pane.createDialog(DialogFactory.defaultFrame(),
title.localized());
DialogShaker shaker = new DialogShaker(dialog);
dialog.pack();
// 这个很关键,一定要设为 false
dialog.setModal(false);
dialog.setVisible(true);
shaker.startShake();
}
......
// 其他方法。
}
原文: http://www.oreilly.com/catalog/swinghks/chapter/hack38.pdf