我创建了一个延时图来显示一天中高速公路上的交通密度。数据保存在double[][]
数组data
中,其中data.length
为2880(每个索引代表30秒的间隔),data[0].length
约为450(代表高速公路路段长度的三次插值)。
我的延时代码如下:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.Timer;
public class TimeLapse extends JPanel {
static double[][] data;
int index = 0;
double max;
double lineWidth;
Timer timer;
final JProgressBar progressBar = new JProgressBar(0, 2879);
BufferedImage[] images;
Color colors[][];
public static void main(String[] args) {
data=getData(); //arbitrary method to get interpolated data
new TimeLapse(data, 90);
}
public TimeLapse(double[][] data1, double max) {
data = data1;
this.max = max;
JFrame frame = new JFrame("Timelapse");
frame.setPreferredSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.NORTH;
c.gridwidth = 1;
c.gridx = 0;
c.gridheight = 1;
c.weightx = 1;
c.weighty = .01;
c.gridy = 0;
frame.add(progressBar, c);
c.anchor = GridBagConstraints.SOUTH;
c.gridy = 1;
c.gridheight = 9;
c.weighty = 1;
frame.add(this, c);
frame.pack();
getColorArray();
frame.setVisible(true);
int dataLength;
dataLength = data.length;
// Make the animation 5 seconds long
int delay = (int) (5000d / dataLength);
timer = new Timer(delay, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateIndex();
repaint();
Toolkit.getDefaultToolkit().sync();
}
});
timer.start();
}
private void getColorArray() {
double cutOff = max / 2;
colors = new Color[data.length][data[0].length];
for (int index = 0; index < data.length; index++) {
for (double x = 0; x < data[0].length; x++) {
colors[index][(int) x] =
getColor(data[index][(int) x], cutOff);
}
}
}
private void updateIndex() {
index = index < data.length - 1 ? index + 1 : 0;
progressBar.setValue(2879 * index / data.length);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int panelHeight = getHeight();
lineWidth = ((double) getWidth()) / ((double) (data[0].length));
// guaranteed counter, as doing it in timer's ActionListener could overlap with rendering
for (double x = 0; x < data[0].length; x++) {
g2.setColor(colors[index][(int) x]);
double rectHeight = panelHeight * data[index][(int) x] / max;
g2.fillRect((int) (x * lineWidth),
(int) (panelHeight - rectHeight),
(int) (lineWidth + 1), (int) rectHeight + 1);
}
g2.dispose();
}
private Color getColor(double value, double cutOff) {
int hueR, hueG, hueB = 0;
if (value < cutOff) {
hueG = 255;
hueR = (int) (255 * value / (cutOff));
} else if (max != cutOff) {
hueR = 255;
hueG = (int) (255 - (255 * (value - cutOff) / (max - cutOff)));
} else {
hueR = 255;
hueG = 0;
}
hueR = (hueR < 0) ? 0 : ((hueR > 255) ? 255 : hueR);
hueG = (hueG < 0) ? 0 : ((hueG > 255) ? 255 : hueG);
hueB = (hueB < 0) ? 0 : ((hueB > 255) ? 255 : hueB);
return new Color(hueR, hueG, hueB);
}
}
动画运行流畅,但它通常需要比我设置的5秒长得多,我将其归因于面板中数百条线的不断着色和生成。
为了验证我是正确的,它确实比它应该慢得多,我使用了谷歌Chrome插件,当你搜索"秒表"时出现的时间。这样做后,我发现当我运行秒表时,动画速度大大加快,每当我将鼠标移动到某些元素(超链接、顶部的选项卡,以及似乎任何其他对鼠标悬停产生视觉响应的元素)上时也是如此。只有当我移动鼠标或运行秒表时才会发生这种情况;保持鼠标仍然不加速,它似乎只有在Chrome上悬停时才有这种行为(即任何其他应用程序都很好)。有人能解释一下这种奇怪的行为吗?
编辑:它也发生在重新加载选项卡时,但不是在它完成重新加载后。
编辑2:我现在确切地知道计时器正在加速。我创建了一个带有计时器的小类,它每毫秒打印一个递增索引:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TimerTest {
static int index = 0;
public static void main(String[] args) {
Timer t = new Timer(1, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(index++);
}
});
t.start();
while (true) {
}
}
}
这与time lapse类的行为完全相同,当将鼠标移动到Chrome元素上时,速度会大大加快。我认为原因是,在这种情况下,println
是一个缓慢的方法,执行速度比计时器更新慢。还是那句话,有人能解释一下为什么Chrome会特别加快备份计时器的速度吗?
绘画方法仅用于绘画。
你不应该在绘画方法中改变类的属性。
也就是说,您无法控制Swing何时确定需要重新绘制组件。因此,可能会有一些系统调用导致组件被重新绘制,从而比您想象的更频繁地更改属性。
例如,你不应该更新你的"index"变量或进度条值。相反,你的计时器应该调用一个方法来改变这些属性,然后该方法应该调用面板上的重绘。
只有当我移动鼠标
也许你在面板上有工具提示,这会导致它被重新绘制。
这很容易测试,只需在paintComponent()方法中添加System.out.println(…)语句,看看它是否比5秒的计时器显示得更频繁。