Java Swing动画看起来很不流畅。如何让它看起来更专业?

10

更新:稍微复杂的动画加上swing计时器会变得一团糟。问题的根源是Java计时器,无论是swing还是utility版本都不可靠,特别是在跨操作系统性能比较时。通过实现一个普通线程,该程序在所有系统上都可以平稳运行。http://zetcode.com/tutorials/javagamestutorial/animation/。此外,在paintComponent()方法中添加Toolkit.getDefaultToolkit().sync()显著有助于改善情况。

I wrote some code that animated smoothly in an awt.Applet (but flickered), then I refactored it to java swing. Now it doesn't flicker but it looks choppy. I've messed with the timer but that doesn't work. Any tips or suggestions for smoothly animating swing components would be greatly appreciated.


import java.util.Random;
import java.util.ArrayList;
import java.awt.event.<em>;
import java.awt.</em>;
import javax.swing.*;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

公共类"Ball"继承自JApplet{

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            JFrame frame = new JFrame();
            frame.setTitle("And so the ball rolls");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            initContainer(frame);
            frame.pack();
            frame.setVisible(true);
        }
    });
}
public static void initContainer(Container container){

   GraphicsPanel graphicsPanel = new GraphicsPanel();
   MainPanel mainPanel = new MainPanel(graphicsPanel);
   container.add(mainPanel);
   graphicsPanel.startTimer();

}

@Override
public void init(){
    initContainer(this);
}

/////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// 主面板类 MainPanel 继承自 JPanel { JLabel label = new JLabel("粒子"); GraphicsPanel gPanel;

    public MainPanel(GraphicsPanel gPanel){
        this.gPanel = gPanel;
        add(gPanel);
        add(label);
    }

///////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////

类GraphicsPanel继承自JPanel,实现了MouseListener接口。

    private ArrayList<Particle> ballArr = new ArrayList<Particle>();
    private String state="s";         //"s"=spiral, "p"=particle
    private int speed=10;             //~20 Hz
    private Timer timer;

    public GraphicsPanel(){
        System.out.println("echo from gpanel");
        setPreferredSize(new Dimension(500,500));
        timer = new Timer(speed, new TimerListener());
        addMouseListener(this);
    }

    public void startTimer(){
        timer.start();
    }

    @Override
    public void paintComponent(Graphics g){

        super.paintComponent(g);
         for (Particle b: ballArr){
              g.setColor(b.getColor());
              g.fillOval(b.getXCoor(),b.getYCoor(),
                         b.getTheSize(),b.getTheSize());
         }
    }

    public void mousePressed(MouseEvent e) {
        ballArr.add(new Particle(e.getX(), e.getY(), state));
    }
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mouseClicked(MouseEvent e) {}

    class TimerListener implements ActionListener {
        public void actionPerformed(ActionEvent e){
             for (Particle b: ballArr)
                 b.move();
             setBackground(Color.WHITE);
             repaint();

        }
    }

////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// 类 粒子 { private static int instanceCount; {{instanceCount++;}} private int z = 11, t=1, u=1; private int[] RGB = new int[3]; private int[] randomizeColor = new int[3]; private double radius, theta; private int x, y, centerX, centerY, size, spiralDirection=1, ballSizeLowerBound, ballSizeUpperBound, radiusLowerBound, radiusUpperBound, mouseInputX, mouseInputY, radiusXMultiplier, radiusYMultiplier; private Color color; private String state; private Random random = new Random(); /////////////////////////////////////////////////////////////////////////// public Particle(int x, int y, int centerX, int centerY, int radius, int theta, int size, Color color){ this.x=x;this.y=y;this.centerX=centerX;this.centerY=centerY; this.radius=radius;this.theta=theta;this.size=size;this.color=color; }

public Particle(int mouseInputX, int mouseInputY, String state){ this.mouseInputX=mouseInputX; this.mouseInputY=mouseInputY; this.state=state; //随机颜色 RGB[0] = random.nextInt(252); RGB[1] = random.nextInt(252); RGB[2] = random.nextInt(252); randomizeColor[0] = 1+random.nextInt(3); randomizeColor[0] = 1+random.nextInt(3); randomizeColor[0] = 1+random.nextInt(3); centerX=mouseInputX; centerY=mouseInputY; if (state.equals("s")){ //设置螺旋状态 ballSizeLowerBound=5; ballSizeUpperBound=18; radiusLowerBound=0; radiusUpperBound=50; radiusXMultiplier=1; radiusYMultiplier=1; } if (state.equals("p")){ //设置粒子状态 ballSizeLowerBound = 15; ballSizeUpperBound =20 + random.nextInt(15); radiusLowerBound = 5; radiusUpperBound = 15+ random.nextInt(34); radiusXMultiplier=1 + random.nextInt(3); radiusYMultiplier=1 + random.nextInt(3); }
size = ballSizeUpperBound-1; //球的大小 radius = radiusUpperBound-1;
if (instanceCount %2 == 0) // 螺旋方向交替 spiralDirection=-spiralDirection; } /////////////////////////////////////////////////////////////////////////// public int getXCoor(){return centerX+x*spiralDirection;} public int getYCoor(){return centerY+y;} public int getTheSize(){return size;} public Color getColor(){return color;} ////////////////////////////////////////////////////////////////////////// void move(){
//螺旋: 在边界处,dr/dt的变化 if (radius > radiusUpperBound || radius < radiusLowerBound) u = -u;
//螺旋形状公式: 极坐标方程为半径=角度 x = (int) (radius * radiusXMultiplier * Math.cos(theta)); y = (int) (radius * radiusYMultiplier * Math.sin(theta));
radius += .1*u; theta += .1;
//球的大小公式 if (size == ballSizeUpperBound || size == ballSizeLowerBound) t = -t; size += t;
//球的颜色变化 for (int i = 0; i < RGB.length; i++) if (RGB[i] >= 250 || RGB[i] <= 4) randomizeColor[i] = -randomizeColor[i];
RGB[0]+= randomizeColor[0]; RGB[1]+= randomizeColor[1]; RGB[2]+= randomizeColor[2]; color = new Color(RGB[0],RGB[1],RGB[2]); }

}


似乎这个问题也在OTN上被问到了(http://forums.oracle.com/forums/thread.jspa?threadID=2123460)。 - Andrew Thompson
4
安德鲁·汤普森:论坛警察 - comp sci balla
遇到了同样的问题...虽然基于线程的方法有所帮助,但是您提供的使用Toolkit.getDefaultToolkit().sync()的提示真的让我的动画更加流畅。谢谢! - nedblorf
1个回答

4

不要设置一个恒定的间隔计时器。在处理程序中将计时器设置为一次性触发。

  1. 获取当前时间(保存在frameStartTime中)
  2. 执行你的帧
  3. 设置计时器在以下时间内触发:interval - (newCurrentTime - frameStartTime)

这样会更加平滑。如果你想真正成为专业人士(并且保持在Java中),我认为你需要考虑JavaFX。


3
考虑使用JavaFX吗? :) 真的吗? :) - willcodejavaforfood
4
在indeed.com上搜索""java fx""的工作,全国共返回了16个结果。 - comp sci balla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接