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

Java 如何在同一面板上创建具有两个不同时间间隔的计时器/动作事件的多个动画组件?,java,swing,layout,jpanel,paintcomponent,Java,Swing,Layout,Jpanel,Paintcomponent,我需要一个简单的动画作业的帮助。它是这样的 我在一个JPanel上有两个刹车灯,目标是让它们有不同的时间间隔,即在不同的时间有灯光循环 如果我一次只有一盏灯,一切都很好。我对这方面比较陌生,但我相信我知道问题所在 在本文下面的代码中,我多次使用this。我相信我的问题发生在public void cycle()方法中,它只是说this.repaint();我有一种感觉,面板是在两个不同的时间段重新喷漆的,它给了我一个随机的光线变化,而不是一个很好的循环 有没有一种方法可以让这两个组件在同一个JP

我需要一个简单的动画作业的帮助。它是这样的

我在一个
JPanel
上有两个刹车灯,目标是让它们有不同的时间间隔,即在不同的时间有灯光循环

如果我一次只有一盏灯,一切都很好。我对这方面比较陌生,但我相信我知道问题所在

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

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


以这种方式运行两个计时器是可以实现的。就我个人而言,我会编写一个“信号”类,用它自己的计时和绘制例程控制单个灯光,但这不是您所要求的

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

然后需要修改绘制代码以检测这些状态并采取适当的操作…例如

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();
    }        
}
公共类交通灯01扩展JPanel{
公共静态最终整数半径=20;
私人定时器;
私有int状态=0;
公共交通灯01(){
计时器=新计时器(500,新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
状态++;
如果(状态>2){
状态=0;
}
重新油漆();
}
});
timer.start();
}
@凌驾
公共维度getPreferredSize(){
返回新尺寸(半径,(半径+1)*3);
}
@凌驾
受保护组件(图形g){
超级组件(g);
Graphics2D g2d=(Graphics2D)g.create();
int半径=20;
Ellipse2D灯光=新的Ellipse2D.Float(0,0,半径,半径);
intx=(getWidth()-radius)/2;
int y=((getHeight()-(radius*3))/2)+(radius*2);
颜色[]=新颜色[]{Color.RED,Color.YELLOW,Color.GREEN};
for(int index=0;index
或者为每个灯光提供可变间隔

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();
    }        
}
公共静态类TraficLight02扩展JPanel{
公共静态最终整数半径=20;
私人定时器;
私有int状态=0;
//绿色,黄色,红色
私有int[]区间=新int[]{30005003000};
公共交通灯02(){
计时器=新计时器(间隔[0],新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
timer.stop();
状态++;
如果(状态>2){
状态=0;
}
timer.setInitialDelay(间隔[状态]);
重新油漆();
timer.restart();
}
});
timer.start();
timer.setRepeats(假);
}
@凌驾
公共维度getPreferredSize(){
返回新尺寸(半径,(半径+1)*3);
}
@凌驾
受保护组件(图形g){
超级组件(g);
Graphics2D g2d=(Graphics2D)g.create();
int半径=20;
Ellipse2D灯光=新的Ellipse2D.Float(0,0,半径,半径);
intx=(getWidth()-radius)/2;
int y=((getHeight()-(radius*3))/2)+(radius*2);
颜色[]=新颜色[]{Color.GREEN,Color.YELLOW,Color.RED};
for(int index=0;index
通过
setter
方法改变这两个概念,以可变的间隔为它们播种并不需要太多


同样,您可以使用三个
定时器,每次一个定时器触发,您只需启动下一个定时器即可。通过这种方式,您可以定义一个计时器链,每个计时器在完成其父计时器后以不同的间隔触发…

可以实现以这种方式运行两个计时器。就我个人而言,我会编写一个“信号”类,用它自己的计时和绘制例程控制单个灯光,但这不是您所要求的

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

然后需要修改绘制代码以检测这些状态并采取适当的操作…例如

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();
        }
    }
}
已更新

更好的解决方案(如我所说)
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