我目前正在使用 AWT Package 和 Swing 在 java 中制作模拟时钟。但数字时钟覆盖目前每隔一段时间就会闪烁一次,我想解决这个问题。
我读过有关与 repaint() 结合实现双缓冲的内容,但我不知道如何实现它。
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
public class Clock2d extends Applet {
GregorianCalendar cal;
Timer clockTimer = new Timer();
TimeZone clockTimeZone = TimeZone.getDefault();
public Clock2d() {
clockTimer.schedule(new TickTimerTask(), 0, 1000);
}
@Override
public void init() {
}
public void paint(Graphics g) {
g.setColor(Color.BLUE);
g.fillOval(40, 40, 220, 220);
g.setColor(Color.WHITE);
g.fillOval(50, 50, 200, 200);
double second = cal.get(Calendar.SECOND);
double minute = cal.get(Calendar.MINUTE);
double hours = cal.get(Calendar.HOUR);
for (int i = 0; i < 60; i++) {
int length = 90;
double rad = (i * 6) * (Math.PI) / 180;
if (i % 5 == 0) {
length = 82;
g.setColor(Color.BLUE);
} else {
g.setColor(Color.GRAY);
}
int x = 150 + (int) (95 * Math.cos(rad - (Math.PI / 2)));
int y = 150 + (int) (95 * Math.sin(rad - (Math.PI / 2)));
int x1 = 150 + (int) (length * Math.cos(rad - (Math.PI / 2)));
int y1 = 150 + (int) (length * Math.sin(rad - (Math.PI / 2)));
g.drawLine(x, y, x1, y1);
}
drawHands(g, second, minute, hours);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
g.setColor(Color.BLUE);
g.setFont(new Font("Tahoma", Font.BOLD, 16));
g.drawString(sdf.format(cal.getTime()), 120, 20);
g.setFont(new Font("Arial", Font.BOLD, 10));
}
public void drawHands(Graphics g, double second, double minute, double hours) {
double rSecond = (second * 6) * (Math.PI) / 180;
double rMinute = ((minute + (second / 60)) * 6) * (Math.PI) / 180;
double rHours = ((hours + (minute / 60)) * 30) * (Math.PI) / 180;
g.setColor(Color.RED);
g.drawLine(150, 150, 150 + (int) (100 * Math.cos(rSecond - (Math.PI / 2))), 150 + (int) (100 * Math.sin(rSecond - (Math.PI / 2))));
g.setColor(Color.BLACK);
g.drawLine(150, 150, 150 + (int) (70 * Math.cos(rMinute - (Math.PI / 2))), 150 + (int) (70 * Math.sin((rMinute - (Math.PI / 2)))));
g.drawLine(150, 150, 150 + (int) (50 * Math.cos(rHours - (Math.PI / 2))), 150 + (int) (50 * Math.sin(rHours - (Math.PI / 2))));
}
class TickTimerTask extends TimerTask {
@Override
public void run() {
cal = (GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
repaint();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Clock 2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(330, 330));
Clock2d clock2d = new Clock2d();
clock2d.setPreferredSize(new Dimension(320, 320));
clock2d.init();
frame.setLayout(new BorderLayout());
frame.getContentPane().add(clock2d, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
除了编号数字时钟偶尔闪烁之外,代码完全按照预期工作。
最佳答案
public class Clock2d extends Applet
java.awt.Applet
不仅已被弃用,而且默认情况下也不是双缓冲的。更改它以扩展JPanel
(这是)。- 然后将
public void Paint(Graphics g) {
更改为public void PaintComponent(Graphics g) { super.paintComponent(g);
以尊重绘制链。 class TickTimerTask extends TimerTask
使用javax.swing.Timer
代替。它在事件调度线程上运行,对 GUI 的任何调用都应在 EDT 上进行。
这是代码中表达的这三点。您在这个版本中看到“闪烁”的伪像吗?
import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import javax.swing.*;
public class Clock2d extends JPanel {
TimeZone clockTimeZone = TimeZone.getDefault();
GregorianCalendar cal =
(GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
ActionListener repaintListener = (ActionEvent e) -> {
cal = (GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
repaint();
};
Timer clockTimer = new Timer(100, repaintListener);
public Clock2d() {
clockTimer.start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(40, 40, 220, 220);
g.setColor(Color.WHITE);
g.fillOval(50, 50, 200, 200);
double second = cal.get(Calendar.SECOND);
double minute = cal.get(Calendar.MINUTE);
double hours = cal.get(Calendar.HOUR);
for (int i = 0; i < 60; i++) {
int length = 90;
double rad = (i * 6) * (Math.PI) / 180;
if (i % 5 == 0) {
length = 82;
g.setColor(Color.BLUE);
} else {
g.setColor(Color.GRAY);
}
int x = 150 + (int) (95 * Math.cos(rad - (Math.PI / 2)));
int y = 150 + (int) (95 * Math.sin(rad - (Math.PI / 2)));
int x1 = 150 + (int) (length * Math.cos(rad - (Math.PI / 2)));
int y1 = 150 + (int) (length * Math.sin(rad - (Math.PI / 2)));
g.drawLine(x, y, x1, y1);
}
drawHands(g, second, minute, hours);
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
g.setColor(Color.BLUE);
g.setFont(new Font("Tahoma", Font.BOLD, 16));
g.drawString(sdf.format(cal.getTime()), 120, 20);
g.setFont(new Font("Arial", Font.BOLD, 10));
}
public void drawHands(Graphics g, double second, double minute, double hours) {
double rSecond = (second * 6) * (Math.PI) / 180;
double rMinute = ((minute + (second / 60)) * 6) * (Math.PI) / 180;
double rHours = ((hours + (minute / 60)) * 30) * (Math.PI) / 180;
g.setColor(Color.RED);
g.drawLine(150, 150, 150 + (int) (100 * Math.cos(rSecond - (Math.PI / 2))), 150 + (int) (100 * Math.sin(rSecond - (Math.PI / 2))));
g.setColor(Color.BLACK);
g.drawLine(150, 150, 150 + (int) (70 * Math.cos(rMinute - (Math.PI / 2))), 150 + (int) (70 * Math.sin((rMinute - (Math.PI / 2)))));
g.drawLine(150, 150, 150 + (int) (50 * Math.cos(rHours - (Math.PI / 2))), 150 + (int) (50 * Math.sin(rHours - (Math.PI / 2))));
}
public static void main(String[] args) {
// Swing / AWT GUIs should be created & changed on the EDT ..
Runnable r = () -> {
JFrame frame = new JFrame("Clock 2D");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(330, 330));
Clock2d clock2d = new Clock2d();
clock2d.setPreferredSize(new Dimension(320, 320));
frame.setLayout(new BorderLayout());
frame.getContentPane().add(clock2d, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
};
// .. this is how we ensure that Runnable is on the EDT.
SwingUtilities.invokeLater(r);
}
}
<小时/>
有关字体的更多提示:
g.setFont(new Font("Arial", Font.BOLD, 10));
考虑到paint方法被重复调用,最好在构造类时建立字体(两者)并将它们存储为类的属性(可以在方法中引用)。
但最好使用逻辑字体而不是“Arial”之类的字体,如下所示:
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 10));
这不仅提供编译时检查,而且可以跨操作系统平台进行调整。例如,SANS_SERIF
字体在 Windows 和可能大多数 *nix 盒子上会生成 Arial,但在 OS X 上,默认的 SANS_SERIF
(未修饰)字体是 Helvetica。
另一种字体 Tahoma 的问题更大。同样,它可能出现在许多 Windows 机器上,但最好要么对其进行测试并拥有备份列表,要么在时钟应用程序中提供字体。 (假设您拥有发行权)。
关于java - 如何修复时钟闪烁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58217509/