java - 两个 ScheduledExecutorService 互相覆盖?

标签 java swing graphics executor scheduledexecutorservice

我正在尝试制作两个以不同速率闪烁的闪烁圆圈。我在 Circle 类中使用 ScheduledExecutorService 来调节闪烁,其持续时间由每个 Circle 中的 ms(毫秒)变量设置。

当我单独制作一辆车时,它们以正确的速率闪烁(我将黑色的设置为 1000 毫秒,红色的设置为 10 毫秒)。但是,当我创建它们并将它们添加到我的 JLayeredPane 时,它​​们都会在较短的时间内闪烁。

我不太熟悉 ScheduledExecutorService 的使用,因此如果有人可以帮助我解决问题,我将不胜感激!

import java.awt.Color;
import java.awt.Graphics;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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

public class blinker extends JFrame
{
    JLayeredPane lp = new JLayeredPane();
    public carlight()
    {
        lp.setLayout(new BorderLayout());
        lp.setPreferredSize(new Dimension(450, 450));

        car c1 = new car(new Color(0, 0, 0), "1", 10, 0, 0);
        c1.setOpaque(false);
        car c2 = new car(new Color(255, 0, 0), "2", 1000, 100, 100);
        c2.setOpaque(false);
        c1.setBounds(0, 0, 450, 450);
        c2.setBounds(0, 0, 450, 450);

        lp.add(c2);
        lp.add(c1);

        add(lp);

        setTitle("Carlights");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500, 500);
        setVisible(true);
    }

    public static void main(String[] args)
    {
        carlight cl = new carlight();
    }
}

class Circle extends JPanel
{
    private Color color;
    private String name;
    private long ms;
    private int x, y;
    private boolean on = true;
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponents(g);
        if(on)
        {
            g.setColor(color);
            int r = 50;
            g.fillOval(x, y, r, r);
            on = false;
        }
        else
        {
            on = true;
        }
    }

    public car(Color c, String s, long l, int x, int y)
    {
        color = c;
        name = s;
        ms = l;
        this.x = x;
        this.y = y;

        this.service.scheduleAtFixedRate(new Runnable()
        {
            public void run()
            {
                repaint();
            }
        }, 0, ms, TimeUnit.MILLISECONDS);
    }
}

最佳答案

您的问题是您在 PaintComponent 方法中具有程序逻辑,您可以在其中更改 boolean 变量的状态。您无法完全控制何时或是否调用此方法,事实上,当调用 repaint 时,将调用 bothpaintComponents,这就是您的信号灯不起作用的原因。解决方案:通过在其他地方更改 boolean 字段 on 的状态,从 PaintComponent 方法中获取逻辑。此外,您还需要使用 Swing 计时器来实现更好的 Swing 线程。

您还需要修复布局的使用,包括避免使用 setBounds。在您的设置中,将其与 BorderLayout 一起使用时,这尤其危险且不可预测。就我个人而言,我不会让 Circle 类扩展 JPanel,而是使其成为一个逻辑类,而不是组件类,然后我将拥有绘图组件,一个扩展 JPanel 的类,保存 Circle 类的实例,然后在其 PaintComponent 中绘制它们。例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

@SuppressWarnings("serial")
public class BlinkerEg extends JPanel {
   private static final int PREF_W = 450;
   private static final int PREF_H = PREF_W;
   private List<Circle> circles = new ArrayList<>();

   public BlinkerEg() {
      circles.add(new Circle(Color.red, 1000, 0, 0, 450, this));
      circles.add(new Circle(Color.black, 60, 0, 0, 450, this));
   }

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

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      for (Circle circle : circles) {
         circle.paint(g2);
      }
   }

   private static void createAndShowGui() {
      BlinkerEg mainPanel = new BlinkerEg();

      JFrame frame = new JFrame("BlinkerEg");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class Circle {
   private Color color;
   private int x, y;
   private int diam;
   private JComponent component;
   private boolean on = true;

   public Circle(Color color, int ms, int x, int y, int diam, JComponent component) {
      this.color = color;
      this.x = x;
      this.y = y;
      this.diam = diam;
      this.component = component;

      new Timer(ms, new TimerListener()).start();
   }

   public void paint(Graphics g) {
      if (on) {
         g.setColor(color);
         g.fillOval(x, y, diam, diam);
      }
   }

   public boolean isOn() {
      return on;
   }

   public void setOn(boolean on) {
      this.on = on;
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         setOn(!isOn());
         component.repaint();
      }
   }
}

关于java - 两个 ScheduledExecutorService 互相覆盖?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26568614/

相关文章:

events - 扩展面板中的 Scala Swing react

c# - 如何在 WPF 中指定 Canvas 上椭圆形状的位置?

c++ - opengl 光线追踪和网格

python - 将平面上的 3D 坐标转置到新的 2D 坐标系

java - 使用几个曲线球在 Swing JTextArea 上强制执行最大字符数

java - openfaces 树表缺少 js

java - 从 JTextPane 获取 html - 缺少内容文本

java.lang.Object 不能转换为 int

java - 在java中序列化可变对象

java - 按字母顺序显示国家/地区?