javax.swing.Timer slowdown in Java7u40
同时调用 javax.swing.Timer#start(),
7u25 没问题。
但是7u40是个大问题。
调用 ActionListener#actionPerformed 过于滞后。 (基本上同时调用 u25)
u25 和 u40 之间的移动完全不同。 (我使用的是 Windows 8)
我报告了错误,但仍未添加错误跟踪系统。甲骨文粉碎摇摆应用程序?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | import java.awt.*; import java.awt.event.*; import javax.swing.*; public class TimerProblem extends JComponent { int red = 0; TimerProblem(final long startMs) { setPreferredSize(new Dimension(10, 10)); Timer t = new Timer(16, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { red = (int)(System.currentTimeMillis() - startMs) % 255; repaint(); } }); t.setInitialDelay(1000); t.start(); } @Override protected void paintComponent(Graphics g) { g.setColor(new Color(red, 255 - red, 0)); g.fillRect(0, 0, getWidth(), getHeight()); } public static void main(String[] args) { JFrame f = new JFrame(); Container c = f.getContentPane(); c.setLayout(new GridLayout(10, 10)); long startMs = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { c.add(new TimerProblem(startMs)); } f.pack(); f.setVisible(true); } } |
您的示例中出现了几个问题:
-
Swing GUI 对象只能在事件调度线程上构建和操作。
-
所有 Swing
Timer 实例共享一个公共线程,该线程正在饱和。
根据目标,一些替代方案是可能的:
-
使用单个
Timer 实例,并选择一些分数以按比例更高的速率进行更新。下面的示例随机选择N 个组件并每 100 毫秒更新一次。 -
使用
TexturePaint ,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import javax.swing.*; /** @see https://stackoverflow.com/a/18936444/230513 */ public class BlinkenLights { private static final int S = 24; private static final int N = 10; private static final Random r = new Random(); private static final List<MyComponent> list = new ArrayList<MyComponent>(); private static final class MyComponent extends JComponent { public MyComponent() { this.setOpaque(true); } @Override public Dimension getPreferredSize() { return new Dimension(S, S); } @Override protected void paintComponent(Graphics g) { g.setColor(Color.getHSBColor(r.nextFloat() / 6, 1, 1)); g.fillRect(0, 0, getWidth(), getHeight()); } } private static JPanel createPanel() { final JPanel p = new JPanel(); p.setLayout(new GridLayout(N, N)); for (int i = 0; i < N * N; i++) { MyComponent c = new MyComponent(); p.add(c); list.add(c); } Timer t = new Timer(1000 / N, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Collections.shuffle(list, r); for (int i = 0; i < N; i++) { list.get(i).repaint(); } } }); t.start(); return p; } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(createPanel()); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } }); } } |
终于写了DIY重绘管理类.. :(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.Timer; /** * EffectTimer */ public class EffectTimer { /** * All of effect timers in instance of this class. */ static class GlobalTimer implements ActionListener { List<EffectTimer> registeredEffects = new ArrayList<>(); Timer timer = new Timer(16, this); public void start(final EffectTimer t) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { internalStart(t); } }); } void internalStart(EffectTimer t) { int initialDelay = Math.max(0, (int) (t.getEffectStartTime() - System.currentTimeMillis())); if(timer.getInitialDelay() >= initialDelay) { timer.setInitialDelay(initialDelay); } if(!registeredEffects.contains(t)) { registeredEffects.add(t); if(registeredEffects.size() == 1) { timer.start(); } } } void stop(final EffectTimer t) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { registeredEffects.remove(t); checkStop(); } }); } @Override public void actionPerformed(ActionEvent e) { long now = e.getWhen(); Iterator<EffectTimer> iter = registeredEffects.iterator(); while(iter.hasNext()) { EffectTimer t = iter.next(); long elapsedMs = now - t.getEffectStartTime(); if(elapsedMs > 0) { float p = elapsedMs / (float)t.getEffectLengthMs(); if(p >= 1.0f) { iter.remove(); t.stop(); } else { if(t.isReversed()) { p = 1.0f - p; } t.progressChanged(p); } } } checkStop(); } void checkStop() { if(registeredEffects.isEmpty()) { timer.stop(); } } public int getRunningTimerCount() { return registeredEffects.size(); } public void stopAll() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { registeredEffects.clear(); checkStop(); } }); } } static final GlobalTimer GTIMER = new GlobalTimer(); int effectLengthMs = -1; long effectStartMs = -1; float progress = 0.0f; boolean reversed = true; public long getEffectStartTime() { return effectStartMs; } public int getEffectLengthMs() { return effectLengthMs; } public void start(int lengthMs) { start(lengthMs, System.currentTimeMillis()); } public void start(int lengthMs, long startMs) { effectLengthMs = lengthMs; effectStartMs = startMs; reversed = false; progress = 0.0f; GTIMER.start(this); } public boolean isReversed() { return reversed; } public void reverse(final int lengthMs) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { internalReverse(lengthMs); } }); } void internalReverse(int lengthMs) { reversed = !reversed; effectLengthMs = lengthMs; int adjust = reversed ? (int)(lengthMs * (1.0f - progress)) : (int)(lengthMs * progress); effectStartMs = System.currentTimeMillis() - adjust; GTIMER.start(this); } final public void progressChanged(float p) { progress = p; run(p); } /** * 0.0f to 1.0f effect progress. * <wyn>Float.compare(progress, 1.0f) >= 0</wyn> to end progress. */ protected void run(float p) {} public void stop() { progress = reversed ? 0.0f : 1.0f; GTIMER.stop(this); } public boolean isRunning() { return 0.0f < progress && progress < 1.0f; } public float getProgress() { return progress; } public static int getRunningTimerCount() { return GTIMER.getRunningTimerCount(); } public static void stopAll() { GTIMER.stopAll(); } } |