【问题标题】:Java analogue clockJava 模拟时钟
【发布时间】:2018-09-19 06:43:43
【问题描述】:

我正在尝试在 java 中实现一个简单的模拟时钟。我的时钟成功显示了我第一次运行程序时的时间,但是在打开时时间保持不变,并且没有任何绘图更新并且它冻结。我不明白为什么它不起作用,我浏览了论坛试图找到有类似问题的人但不能。如果有人能找到我哪里出错了,我将不胜感激,谢谢

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing. JPanel;

public class ClockFace extends JPanel {

Date date = new Date();

private BufferedImage clockFace;

public ClockFace() {

    this.init();
    this.startClock();

}

private void init() {

    JFrame window = new JFrame("Clock");
    window.setContentPane(this);

    this.setPreferredSize(new Dimension(600,600));

    window.pack();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setVisible(true);
    window.setLocationRelativeTo(null);

}

//Draws the image to the ClockFace panel
public void paintComponent(Graphics g) {

    Graphics2D twoD = (Graphics2D) g;
    twoD.drawImage(clockFace, 0, 0, null);

}

public void startClock() {

    while(true) {

        int seconds = date.getSeconds();
        int minutes = date.getMinutes();
        int hours = date.getHours();

        clockFace = new BufferedImage(600,600,BufferedImage.TYPE_INT_ARGB);
        Graphics2D twoD = clockFace.createGraphics();
        twoD.setColor(Color.BLACK);
        twoD.fillRect(0, 0, 600, 600);
        twoD.setColor(Color.WHITE);
        twoD.translate(300,300);

        //Drawing the hour markers
        for(int i=0; i<12; i++) {

            twoD.drawLine(0, -260, 0, -300);
            twoD.rotate(Math.PI/6);

        }

        twoD.rotate(seconds*Math.PI/30);
        twoD.drawLine(0, 0, 0, -290);

        twoD.rotate(2*Math.PI-seconds*Math.PI/30);
        twoD.rotate(minutes*Math.PI/30);
        twoD.setStroke(new BasicStroke(3));
        twoD.drawLine(0, 0, 0, -250);

        twoD.rotate(2*Math.PI-minutes*Math.PI/30);
        twoD.rotate(hours*Math.PI/6);
        twoD.setStroke(new BasicStroke(6));
        twoD.drawLine(0, 0, 0, -200);

        repaint();

        //Pausing until the next "tick"
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

}

【问题讨论】:

  • getDate 方法已弃用。相反,我建议您使用 LocalTime,这是现代 Java 日期和时间 API java.time 中的类之一。另外,您是否曾经更改过date 的值? Link: Oracle Tutorial: Date Time.

标签: java swing time graphics clock


【解决方案1】:

您正在阻塞事件调度线程,阻止它处理任何新的输入事件或处理任何新的绘制事件

更多详情请见Concurrency in Swing

所以不是...

public void startClock() {
    while(true) {
       //...
    }
}

你应该使用更像...的东西

public void startClock() {
    Timer timer = new Timer(500, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            //... Update time and schedule repaint
        }
    }
}

详情请见How to Use Swing Timers

你还会发现这个……

//Draws the image to the ClockFace panel
public void paintComponent(Graphics g) {

    Graphics2D twoD = (Graphics2D) g;
    twoD.drawImage(clockFace, 0, 0, null);

}

给你问题(油漆文物)。应该是..

//Draws the image to the ClockFace panel
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D twoD = (Graphics2D) g;
    twoD.drawImage(clockFace, 0, 0, null);

}

更新...

关于天气的讨论(太多)了,核心问题与事件调度线程的潜在阻塞或Date 类的不可变性质有关。

事实是,可能两者兼而有之。

java.util.Date 是一个时间点的快照(当它被创建时),它不会改变也不会更新。因此,根据您的示例,您必须在循环的每次迭代中更新 date 实例。

更好的解决方案可能是使用java.time.LocalTime,除了更新,它通常更易于使用(恕我直言)

例如...

import java.awt.BasicStroke;
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.image.BufferedImage;
import java.time.LocalTime;
import java.time.temporal.TemporalField;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

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

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

    public class ClockFace extends JPanel {

        public ClockFace() {
            this.startClock();
        }

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

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

            LocalTime now = LocalTime.now();
            int seconds = now.getSecond();
            int minutes = now.getMinute();
            int hours = now.getHour();

            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, 0, 600, 600);
            g2d.setColor(Color.WHITE);
            g2d.translate(300, 300);

            //Drawing the hour markers
            for (int i = 0; i < 12; i++) {

                g2d.drawLine(0, -260, 0, -300);
                g2d.rotate(Math.PI / 6);

            }

            g2d.rotate(seconds * Math.PI / 30);
            g2d.drawLine(0, 0, 0, -290);

            g2d.rotate(2 * Math.PI - seconds * Math.PI / 30);
            g2d.rotate(minutes * Math.PI / 30);
            g2d.setStroke(new BasicStroke(3));
            g2d.drawLine(0, 0, 0, -250);

            g2d.rotate(2 * Math.PI - minutes * Math.PI / 30);
            g2d.rotate(hours * Math.PI / 6);
            g2d.setStroke(new BasicStroke(6));
            g2d.drawLine(0, 0, 0, -200);

            g2d.dispose();
        }

        public void startClock() {
            Timer timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            });
            timer.start();
        }

    }

}

【讨论】:

  • 使用javax.swing.Timer 是这种情况的最佳选择。但是,一般来说,当您想做一些需要很长时间(并且可能会阻塞 EDT)的事情时,请查看 SwingWorker,这是一个通用案例类,适用于比 Timer 不那么专业的后台任务。 docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
  • 他实际上并没有阻止 EDT;睡眠提供了足够的时间让它运行。但这不是这样做的方法。
  • @markspace 请记住,SwingWorker 有额外的同步开销(并且由于实现对后台线程的影响更大)比Timer 之类的规则要少得多
  • @laune 他的ClockFace 类扩展了JPanel。如果他没有在 EDT 上打电话给它的 ctor,那就错了。 ctor 调用 while 循环,这意味着它将阻塞 EDT。
  • @laune 在这种情况下,如果 OP 在 EDT 的上下文中执行代码(并根据它们的描述),那么 Thread.sleep 将使 EDT 休眠 - 防止它处理任何新的绘制方法 - 此外,while-loop 将继续阻止 EDT 能够处理事件队列,因为该方法永远不存在
【解决方案2】:

在while(true)之后插入date的计算。

这不是编写程序执行按时间增量安排的任务的方法。它只解决了你眼前的问题。查看其他答案。

【讨论】:

  • 90% 确定这是错误的答案。 “阻止 EDT”是正确的想法。
  • 嗯,它从晚上 9:30 开始运行。在我的系统上。睡眠解除阻塞。并不是说我会那样写...计时器,以及所有这些...
  • @markspace 这里没有 EDT 阻塞... startclock() 不是从 EDT 而是从主线程调用的。问题是日期永远不会重新计算...
  • @laune 对日期的计算更精确,你应该说日期是不可变的,所以 OP 每次需要确定当前日期时都需要构造一个新的日期对象日期/时间。
  • @Jean-BaptisteYunès 我想说这是两者的原因:P
猜你喜欢
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多