在这个例子里,我们会看到很多五颜六色的泡泡出现在屏幕上,逐渐变大然后消失,先来张截图看看效果
效果还不错吧
程序的架构很简单,从JPanel继承并重写paintComponent,然后将其设置成JFrame的ContentPane即可
先来看看架构代码,具体的绘制代码稍后奉上
public class MyBubbles extends JPanel { @Override protected void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponent(g); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { JFrame frame=new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setContentPane(new MyBubbles()); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } }
这些代码很简单,做过swing的童鞋应该毫无压力,需要注意的有两点:
1、setLocationRelativeTo方法可以很方便的将窗体居中
2、初始化窗体的代码是交给SwingUtilities来执行的,这涉及到swing的线程安全问题,这个话题很大,我在这就不赘述了,感兴趣的童鞋请猛戳这里:http://twaver.servasoft.com/?p=531
ok,我们开始思考怎样做成这种效果
首先气泡的个数和最大size都是固定的,代码如下:
Ellipse2D.Double[] ellipses;//气泡数组 int maxSize=60;//气泡最大像素
我们用椭圆来表示气泡,即Ellipse2D,Ellipse2D.Double是Ellipse2D的子类,它的坐标和大小都是double表示的,此外Ellipse2D还有个子类叫Ellipse2D.Float,顾名思义,它的坐标和大小用float来表示。
在构造方法中,我们初始化气泡数组
this.setBackground(Color.BLACK);//背景色 ellipses=new Ellipse2D.Double[25]; for(int i=0;i<ellipses.length;i++){ ellipses[i]=new Ellipse2D.Double(); double size=Math.random()*maxSize; this.setRandomXY(i, size, 400, 400); }
private void setRandomXY(int i,double size,double width,double height){ double x=Math.random()*(width-maxSize/2); double y=Math.random()*(height-maxSize/2); ellipses[i].setFrame(x, y, size,size); }
为了营造气泡大小不一的效果,我们使用随机数产生气泡size,为了实现代码重用,我们将设置气泡尺寸和位置的相关代码抽取到一个单独的方法中。
动画效果使用javax.swing.Timer实现(注意不是java.util.Timer),定时重绘气泡,改变气泡的尺寸。
在构造方法中创建一个Timer
timer=new Timer(30, this); timer.start();
为了简化代码,我们的MyBubbles实现了ActionListener接口,将自身交给timer调用。调用的代码很简单,只有一句话:repaint();
终于到了最关键的绘制部分了
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d=(Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
step(this.getWidth(),this.getHeight());
render((Graphics2D)g);
}
private void step(double w,double h){
for(int i=0;i<ellipses.length;i++){
if(ellipses[i].width>maxSize){
setRandomXY(i,1,w,h);
}else{
ellipses[i].setFrame(ellipses[i].getX(),ellipses[i].getY(), ellipses[i].getWidth()+1, ellipses[i].getHeight()+1);
}
}
}
private void render(Graphics2D g){
for(int i=0;i<ellipses.length;i++){
g.setColor(colors[i%colors.length]);
g.setStroke(new BasicStroke(2));
g.draw(ellipses[i]);
}
}
paintComponent方法是我们重写JPanel的,每当Timer调用到repaint()时,这个方法也会马上被调用。
我们将改变气泡的尺寸的方法放到step中,将绘制方法放到render中,这样整个绘制过程看起来就很清晰了:
在step中,我们遍历气泡数组增大尺寸,当尺寸>maxSize时,就调用我们之前的setRandomXY方法重置气泡。
render方法中,遍历气泡设置颜色和画笔,并将气泡画到屏幕上。
我们在绘制的时候打开了反锯齿:g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
如果没有这句代码,画面会惨不忍睹,大家可以自行测试。
完整的代码见附件。