Swing 制作振动窗口

今天在网上看到了一个很有趣的关于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();

    }

public static void main(String asrg[]){
	JFrame frame = new JFrame();
	JButton button = new JButton("打开");
	button.addActionListener(new ActionListener(){

		public void actionPerformed(ActionEvent arg0) {
			JDialog dialog = new JDialog();
			dialog.setVisible(true);
			dialog.setSize(200, 200);
			DialogEarthquakeCenter dialog1 = new DialogEarthquakeCenter(dialog);
			dialog1.startShake();
		}
		
	});
	frame.setSize(500,500);
	frame.setMinimumSize(new Dimension(500,500));
	frame.getContentPane().add(button);
	frame.setVisible(true);
}

}




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 createShakeMessageDialog(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

你可能感兴趣的:(swing)