如何在同一面板上使用具有两个不同时间间隔的计时器/动作事件创建多个动画组件?

时间:2013-06-26 01:07:31

标签: java swing layout jpanel paintcomponent

我需要一个简单的动画分配帮助。它如下。

我在JPanel上有两个停车灯,对象是两个停车灯有不同的时间间隔,即灯光在不同时间循环。

如果我一次只有一盏灯,那么一切正常。我比较新,但我相信我知道这个问题。

在本文下的代码中,我多次使用this。我相信我的问题出现在public void cycle()方法中,它只是说this.repaint();我有一种感觉,面板正在两个不同的时间段重新粉刷,它给了我一些随机的光线变化,而不是一个很好的周期。

我有没有办法让这两个组件在同一个JPanel上使用更具体的重绘方法(可能是各个灯具周围的边界框),或者创建单独的面板是一个更好的选择(和如果是这样的话会有所帮助,因为我了解基本布局,但之前从未使用过它们。)

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

public class DrawingPanel extends JPanel implements Lighter
{
    // instance variables 
    private final int INTERVAL1 = 2000;
    private final int INTERVAL2 = 5000;
    private TrafficLight _light1, _light2;
    private LightTimer _timer1,_timer2;

    /**
     * Constructor for objects of class DrawingPanel
     */
    public DrawingPanel()
    {
        // initialise instance variables
        super();
        this.setBackground(Color.CYAN);
        _light1 = new TrafficLight(50,50);
        _light2 = new TrafficLight(200,50);
        _timer1 = new LightTimer(INTERVAL1,this);
        _timer2 = new LightTimer(INTERVAL2,this);
        _timer1.start();
        _timer2.start();
    }

    public void cycle(){
        _light1.cycle();
        _light2.cycle();
        this.repaint();
        }

        public void paintComponent(Graphics pen)
        {
        super.paintComponent(pen);
        Graphics2D aBetterPen = (Graphics2D)pen;
        _light1.fill(aBetterPen);
        _light2.fill(aBetterPen);
    }

}

2 个答案:

答案 0 :(得分:3)

以时尚的方式运行两个计时器是可以实现的。就个人而言,我会写一个“信号”类,用它自己的时间和绘画程序控制一个灯,但这不是你所问的。

您需要做的是为每个信号维护某种状态变量并单独更新它们。

然后,您需要修改油漆代码以检测这些状态并采取适当的措施......例如

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestBlink {

    public static void main(String[] args) {
        new TestBlink();
    }

    public TestBlink() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Timer blink1;
        private Timer blink2;

        private boolean light1 = false;
        private boolean light2 = false;

        public TestPane() {

            blink1 = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    light1 = !light1;
                    repaint();
                }
            });
            blink2 = new Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    light2 = !light2;
                    repaint();
                }
            });

            blink1.start();
            blink2.start();

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int radius = 20;
            int x = (getWidth() - (radius * 2)) / 2;
            int y = (getHeight() - (radius * 2)) / 2;

            Ellipse2D signal1 = new Ellipse2D.Float(x, y, radius, radius);
            Ellipse2D signal2 = new Ellipse2D.Float(x + radius, y, radius, radius);

            g2d.setColor(Color.RED);
            g2d.draw(signal1);
            if (light1) {
                g2d.fill(signal1);
            }
            g2d.setColor(Color.GREEN);
            g2d.draw(signal2);
            if (light2) {
                g2d.fill(signal2);
            }

            g2d.dispose();
        }
    }
}

<强>更新

更好的解决方案(如前所述)将包含单个类中单个序列的所有逻辑。这隔离了绘画,允许您改变每个序列的个体性质。

例如......

这是一个使用固定变化率的简单例子,因此每盏灯的时间大致相同......

public class TraficLight01 extends JPanel {

    public static final int RADIUS = 20;

    private Timer timer;
    private int state = 0;

    public TraficLight01() {
        timer = new Timer(500, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                state++;
                if (state > 2) {
                    state = 0;
                }
                repaint();
            }
        });
        timer.start();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(RADIUS, (RADIUS + 1) * 3);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        int radius = 20;
        Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
        int x = (getWidth() - radius) / 2;
        int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);

        Color color[] = new Color[]{Color.RED, Color.YELLOW, Color.GREEN};
        for (int index = 0; index < color.length; index++) {
            g2d.translate(x, y);
            g2d.setColor(color[index]);
            g2d.draw(light);
            if (state == index) {
                g2d.fill(light);
            }
            g2d.translate(-x, -y);
            y -= radius + 1;
        }
        g2d.dispose();
    }        
}

或者你为每个灯提供一个可变间隔......

public static class TraficLight02 extends JPanel {

    public static final int RADIUS = 20;

    private Timer timer;
    private int state = 0;

    // Green, Yellow, Red
    private int[] intervals = new int[]{3000, 500, 3000};

    public TraficLight02() {
        timer = new Timer(intervals[0], new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                timer.stop();
                state++;
                if (state > 2) {
                    state = 0;
                }
                timer.setInitialDelay(intervals[state]);
                repaint();
                timer.restart();
            }
        });
        timer.start();
        timer.setRepeats(false);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(RADIUS, (RADIUS + 1) * 3);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        int radius = 20;
        Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
        int x = (getWidth() - radius) / 2;
        int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);

        Color color[] = new Color[]{Color.GREEN, Color.YELLOW, Color.RED};
        for (int index = 0; index < color.length; index++) {
            g2d.translate(x, y);
            g2d.setColor(color[index]);
            g2d.draw(light);
            if (state == index) {
                g2d.fill(light);
            }
            g2d.translate(-x, -y);
            y -= radius + 1;
        }
        g2d.dispose();
    }        
}

通过setter方法改变这两个概念并使用可变间隔对它们进行种子化不会花费太多时间。

同样,你可以使用三个Timer,每次发射一次,你只需启动下一个。通过这种方式,您可以定义一组计时器,每个计时器在完成它的父级后以不同的间隔触发...

答案 1 :(得分:0)

注意,您的评论

  

它给了我一些随机的光线变化而不是一个好的循环。

你期待它看起来像什么?

使用你设置的时间间隔,它可能看起来有点随机,但它实际上正在工作,即你的间隔会像这样工作(我对Interval变量的假设是好的)

Time(s)    1    2   3   4   5   6   7   8   9   10   11   12   13   14   15   16
Light1         ON      OFF     ON      OFF      ON        OFF       ON        OFF
Light2                      ON                  OFF                      ON