3

同时调用 javax.swing.Timer#start(),

7u25没问题。

在此处输入图像描述在此处输入图像描述

但是7u40是个大问题。

在此处输入图像描述在此处输入图像描述

太滞后调用 ActionListener#actionPerformed。(基本上同时调用u25)

u25 和 u40 之间的移动完全不同。(我使用 Windows 8)我报告了错误但仍然没有添加错误跟踪系统。甲骨文粉碎摇摆应用程序?

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);
        }

}
4

2 回答 2

1

您的示例中出现了几个问题:

  • Swing GUI 对象应该事件分派线程上构建和操作。

  • 所有 SwingTimer实例共享一个公共线程,该线程正在饱和。

根据目标,一些替代方案是可能的:

  • 使用单个Timer实例,并选择一些分数以按比例更高的速率进行更新。下面的示例随机选择N组件并每 100 毫秒更新一次。

  • 使用,TexturePaint如图所示

图片

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);
            }
        });
    }
}
于 2013-09-21T19:03:02.503 回答
0

最后我写了 DIY 重绘管理类.. :(

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.
     * <code>Float.compare(progress, 1.0f) >= 0</code> 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();
    }

}
于 2013-09-24T03:12:12.670 回答