Swinganimation运行速度非常慢

我正在使用Java Swing运行当前的animation。 这是一个离散事件模拟和基于文本的模拟工作正常,我只是有模拟连接到GUI输出的问题。

对于这个例子,我将有10辆汽车进行模拟。 这些汽车是由JPanels代表的,我将在稍后详细阐述。

因此,请考虑事件process_car_arrival。 每当这个事件计划执行时,我在我的Model类中添加一个Car对象到一个名为carsArrayListCar类具有以下相关属性:

 Point currentPos; // The current position, initialized in another method when knowing route. double speed; // giving the speed any value still causes the same problem but I have 5 atm. RouteType route; // for this example I only consider one simple route 

另外还有以下方法move()

 switch (this.route) { case EAST: this.currentPos.x -= speed; return this.currentPos; . . . //only above is relevant in this example 

这一切都很好。 所以理论上汽车从东向西沿着一条直线行驶,因为我只是为每一辆我想要移动的汽车调用move()方法。

返回到process_car_arrival事件。 添加一个Car对象后,它调用View类中的方法addCarToEast() 。 这在从东到西的道路开始时增加了一个JPanel。

现在去View类,我有一个**单独的**线程执行以下操作(run()方法):

 @Override public void run() { while (true) { try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } if (!cars.isEmpty()) { cars.get(i).setLocation( new Point(getModel.getCars().get(i).move())); if (i == cars.size() - 1) { i = 0; } else { i++; } } } } 

以上做法首先将车从东向西顺利移动。 但是在3-4辆车移动之后,它的速度会变得极其缓慢,而当我有10辆车的时候,它只会移动很less。

为了清除,目前在Model类中有一个ArrayList对象的ArrayList ,在View类中还有一个代表汽车的JPanel对象的ArrayList 。 我试图将Car对象与JPanels相匹配,但我显然正在做一些艰巨的工作。

我怀疑我正在疯狂地做一些效率低下的事情,但我不知道是什么。 我想最初也许是访问ArrayList这么多,我想会让它真的很慢。

任何指针,我可以改变,使其运行顺利?

根据以前的答案 ,下面的例子模拟了一个随机在矩形网格上移动的三个出租车队。 javax.swing.Timer以5 Hz的频率驱动animation。 模型和视图紧密结合在CabPanel ,但animation可能提供了一些有用的见解。 特别是,您可能会增加驾驶室的数量或降低计时器延迟。

图片

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Point; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; /** * @see https://stackoverflow.com/a/14887457/230513 * @see https://stackoverflow.com/questions/5617027 */ public class FleetPanel extends JPanel { private static final Random random = new Random(); private final MapPanel map = new MapPanel(); private final JPanel control = new JPanel(); private final List<CabPanel> fleet = new ArrayList<CabPanel>(); private final Timer timer = new Timer(200, null); public FleetPanel() { super(new BorderLayout()); fleet.add(new CabPanel("Cab #1", Hue.Cyan)); fleet.add(new CabPanel("Cab #2", Hue.Magenta)); fleet.add(new CabPanel("Cab #3", Hue.Yellow)); control.setLayout(new GridLayout(0, 1)); for (CabPanel cp : fleet) { control.add(cp); timer.addActionListener(cp.listener); } this.add(map, BorderLayout.CENTER); this.add(control, BorderLayout.SOUTH); } public void start() { timer.start(); } private class CabPanel extends JPanel { private static final String format = "000000"; private final DecimalFormat df = new DecimalFormat(format); private JLabel name = new JLabel("", JLabel.CENTER); private Point point = new Point(); private JLabel position = new JLabel(toString(point), JLabel.CENTER); private int blocks; private JLabel odometer = new JLabel(df.format(0), JLabel.CENTER); private final JComboBox colorBox = new JComboBox(); private final JButton reset = new JButton("Reset"); private final ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int ds = random.nextInt(3) - 1; if (random.nextBoolean()) { point.x += ds; } else { point.y += ds; } blocks += Math.abs(ds); update(); } }; public CabPanel(String s, Hue hue) { super(new GridLayout(1, 0)); name.setText(s); this.setBackground(hue.getColor()); this.add(map, BorderLayout.CENTER); for (Hue h : Hue.values()) { colorBox.addItem(h); } colorBox.setSelectedIndex(hue.ordinal()); colorBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Hue h = (Hue) colorBox.getSelectedItem(); CabPanel.this.setBackground(h.getColor()); update(); } }); reset.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { point.setLocation(0, 0); blocks = 0; update(); } }); this.add(name); this.add(odometer); this.add(position); this.add(colorBox); this.add(reset); } private void update() { position.setText(CabPanel.this.toString(point)); odometer.setText(df.format(blocks)); map.repaint(); } private String toString(Point p) { StringBuilder sb = new StringBuilder(); sb.append(Math.abs(px)); sb.append(px < 0 ? " W" : " E"); sb.append(", "); sb.append(Math.abs(py)); sb.append(py < 0 ? " N" : " S"); return sb.toString(); } } private class MapPanel extends JPanel { private static final int SIZE = 16; public MapPanel() { this.setPreferredSize(new Dimension(32 * SIZE, 32 * SIZE)); this.setBackground(Color.lightGray); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int w = this.getWidth(); int h = this.getHeight(); g2d.setColor(Color.gray); for (int col = SIZE; col <= w; col += SIZE) { g2d.drawLine(col, 0, col, h); } for (int row = SIZE; row <= h; row += SIZE) { g2d.drawLine(0, row, w, row); } for (CabPanel cp : fleet) { Point p = cp.point; int x = SIZE * (px + w / 2 / SIZE) - SIZE / 2; int y = SIZE * (py + h / 2 / SIZE) - SIZE / 2; g2d.setColor(cp.getBackground()); g2d.fillOval(x, y, SIZE, SIZE); } } } public enum Hue { Cyan(Color.cyan), Magenta(Color.magenta), Yellow(Color.yellow), Red(Color.red), Green(Color.green), Blue(Color.blue), Orange(Color.orange), Pink(Color.pink); private final Color color; private Hue(Color color) { this.color = color; } public Color getColor() { return color; } } private static void display() { JFrame f = new JFrame("Dispatch"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); FleetPanel fp = new FleetPanel(); f.add(fp); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); fp.start(); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { display(); } }); } } 

我无法抗拒

在这里输入图像描述

我有500辆汽车在屏幕上慢跑(这不是最快的…大约200-300是相当不错的… … –

这使用面板来表示每辆车。 如果你想获得更好的性能,你可能需要考虑使用某种types的后备缓冲区。

 public class TestAnimation10 { public static void main(String[] args) { new TestAnimation10(); } public TestAnimation10() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { } final TrackPane trackPane = new TrackPane(); JSlider slider = new JSlider(1, 500); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { trackPane.setCongestion(((JSlider)e.getSource()).getValue()); } }); slider.setValue(5); JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(trackPane); frame.add(slider, BorderLayout.SOUTH); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TrackPane extends JPanel { private List<Car> cars; private int maxCars = 1; private List<Point2D[]> points; private Ellipse2D areaOfEffect; public TrackPane() { points = new ArrayList<>(25); cars = new ArrayList<>(25); setLayout(null); Timer timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Rectangle bounds = areaOfEffect.getBounds(); List<Car> tmp = new ArrayList<>(cars); for (Car car : tmp) { car.move(); if (!bounds.intersects(car.getBounds())) { remove(car); cars.remove(car); } } updatePool(); repaint(); } }); timer.setRepeats(true); timer.setCoalesce(true); timer.start(); updateAreaOfEffect(); } protected void updateAreaOfEffect() { double radius = Math.max(getWidth(), getHeight()) * 1.5d; double x = (getWidth() - radius) / 2d; double y = (getHeight() - radius) / 2d; areaOfEffect = new Ellipse2D.Double(x, y, radius, radius); } @Override public void invalidate() { super.invalidate(); updateAreaOfEffect(); } protected void updatePool() { while (cars.size() < maxCars) { // if (cars.size() < maxCars) { Car car = new Car(); double direction = car.getDirection(); double startAngle = direction - 180; double radius = areaOfEffect.getWidth(); Point2D startPoint = getPointAt(radius, startAngle); int cx = getWidth() / 2; int cy = getHeight() / 2; double x = cx + (startPoint.getX() - car.getWidth() / 2); double y = cy + (startPoint.getY() - car.getHeight() / 2); car.setLocation((int)x, (int)y); Point2D targetPoint = getPointAt(radius, direction); points.add(new Point2D[]{startPoint, targetPoint}); add(car); cars.add(car); } } @Override public void paint(Graphics g) { super.paint(g); Font font = g.getFont(); font = font.deriveFont(Font.BOLD, 48f); FontMetrics fm = g.getFontMetrics(font); g.setFont(font); g.setColor(Color.RED); String text = Integer.toString(maxCars); int x = getWidth() - fm.stringWidth(text); int y = getHeight() - fm.getHeight() + fm.getAscent(); g.drawString(text, x, y); text = Integer.toString(getComponentCount()); x = getWidth() - fm.stringWidth(text); y -= fm.getHeight(); g.drawString(text, x, y); text = Integer.toString(cars.size()); x = getWidth() - fm.stringWidth(text); y -= fm.getHeight(); g.drawString(text, x, y); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } public void setCongestion(int value) { maxCars = value; } } protected static Point2D getPointAt(double radius, double angle) { double x = Math.round(radius / 2d); double y = Math.round(radius / 2d); double rads = Math.toRadians(-angle); double fullLength = Math.round((radius / 2d)); double xPosy = (Math.cos(rads) * fullLength); double yPosy = (Math.sin(rads) * fullLength); return new Point2D.Double(xPosy, yPosy); } public class Car extends JPanel { private double direction; private double speed; private BufferedImage background; public Car() { setOpaque(false); direction = Math.random() * 360; speed = 5 + (Math.random() * 10); int image = 1 + (int) Math.round(Math.random() * 5); try { String name = "/Car0" + image + ".png"; background = ImageIO.read(getClass().getResource(name)); } catch (IOException ex) { ex.printStackTrace(); } setSize(getPreferredSize()); // setBorder(new LineBorder(Color.RED)); } public void setDirection(double direction) { this.direction = direction; revalidate(); repaint(); } public double getDirection() { return direction; } public void move() { Point at = getLocation(); at.x += (int)(speed * Math.cos(Math.toRadians(-direction))); at.y += (int)(speed * Math.sin(Math.toRadians(-direction))); setLocation(at); } @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); if (background != null) { double radian = Math.toRadians(direction); double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian)); int w = background.getWidth(), h = background.getHeight(); int neww = (int) Math.floor(w * cos + h * sin); int newh = (int) Math.floor(h * cos + w * sin); size = new Dimension(neww, newh); } return size; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); int x = (getWidth() - background.getWidth()) / 2; int y = (getHeight() - background.getHeight()) / 2; g2d.rotate(Math.toRadians(-(direction + 180)), getWidth() / 2, getHeight() / 2); g2d.drawImage(background, x, y, this); g2d.dispose(); // Debug graphics... // int cx = getWidth() / 2; // int cy = getHeight() / 2; // // g2d = (Graphics2D) g.create(); // g2d.setColor(Color.BLUE); // double radius = Math.min(getWidth(), getHeight()); // Point2D pointAt = getPointAt(radius, direction); // g2d.draw(new Ellipse2D.Double(cx - (radius / 2d), cy - (radius / 2d), radius, radius)); // // double xo = cx; // double yo = cy; // double xPos = cx + pointAt.getX(); // double yPos = cy + pointAt.getY(); // // g2d.draw(new Line2D.Double(xo, yo, xPos, yPos)); // g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4)); // g2d.dispose(); } } } 

更新与优化版本

我做了一些代码优化,创build了汽车对象(还有改进的空间),并增加了graphics输出(使它看起来更好)。

基本上,现在,当一辆汽车离开屏幕时,它被放置在一个游泳池里。 当需要另一辆汽车时,如果可能的话,将其从车库中拉出来,否则就会造成一辆新车。 这减less了创build和删除很多(相对)短暂的对象的开销,这使内存使用更稳定一些。

在我的2560×1600分辨率屏幕上(运行最大化),我能够同时运行4500辆汽车。 一旦对象创build减less,它运行得相对顺利(它永远不会跑到10,但它没有遭受速度的显着降低)。

 public class TestAnimation10 { public static void main(String[] args) { new TestAnimation10(); } public TestAnimation10() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { } final TrackPane trackPane = new TrackPane(); JSlider slider = new JSlider(1, 5000); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { trackPane.setCongestion(((JSlider) e.getSource()).getValue()); } }); slider.setValue(5); JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(trackPane); frame.add(slider, BorderLayout.SOUTH); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TrackPane extends JPanel { private List<Car> activeCarList; private List<Car> carPool; private int maxCars = 1; private List<Point2D[]> points; private Ellipse2D areaOfEffect; public TrackPane() { points = new ArrayList<>(25); activeCarList = new ArrayList<>(25); carPool = new ArrayList<>(25); setLayout(null); Timer timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Rectangle bounds = areaOfEffect.getBounds(); List<Car> tmp = new ArrayList<>(activeCarList); for (Car car : tmp) { car.move(); if (!bounds.intersects(car.getBounds())) { remove(car); activeCarList.remove(car); carPool.add(car); } } updatePool(); repaint(); } }); timer.setRepeats(true); timer.setCoalesce(true); timer.start(); updateAreaOfEffect(); } protected void updateAreaOfEffect() { double radius = Math.max(getWidth(), getHeight()) * 1.5d; double x = (getWidth() - radius) / 2d; double y = (getHeight() - radius) / 2d; areaOfEffect = new Ellipse2D.Double(x, y, radius, radius); } @Override public void invalidate() { // super.invalidate(); updateAreaOfEffect(); } protected void updatePool() { if (activeCarList.size() < maxCars) { int count = Math.min(maxCars - activeCarList.size(), 10); for (int index = 0; index < count; index++) { Car car = null; if (carPool.isEmpty()) { car = new Car(); } else { car = carPool.remove(0); } double direction = car.getDirection(); double startAngle = direction - 180; double radius = areaOfEffect.getWidth(); Point2D startPoint = getPointAt(radius, startAngle); int cx = getWidth() / 2; int cy = getHeight() / 2; double x = cx + (startPoint.getX() - car.getWidth() / 2); double y = cy + (startPoint.getY() - car.getHeight() / 2); car.setLocation((int) x, (int) y); Point2D targetPoint = getPointAt(radius, direction); points.add(new Point2D[]{startPoint, targetPoint}); add(car); activeCarList.add(car); } } } @Override public void paint(Graphics g) { super.paint(g); Font font = g.getFont(); font = font.deriveFont(Font.BOLD, 48f); FontMetrics fm = g.getFontMetrics(font); g.setFont(font); g.setColor(Color.RED); String text = Integer.toString(maxCars); int x = getWidth() - fm.stringWidth(text); int y = getHeight() - fm.getHeight() + fm.getAscent(); g.drawString(text, x, y); text = Integer.toString(getComponentCount()); x = getWidth() - fm.stringWidth(text); y -= fm.getHeight(); g.drawString(text, x, y); text = Integer.toString(activeCarList.size()); x = getWidth() - fm.stringWidth(text); y -= fm.getHeight(); g.drawString(text, x, y); text = Integer.toString(carPool.size()); x = getWidth() - fm.stringWidth(text); y -= fm.getHeight(); g.drawString(text, x, y); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } public void setCongestion(int value) { maxCars = value; } @Override public void validate() { } @Override public void revalidate() { } // @Override // public void repaint(long tm, int x, int y, int width, int height) { // } // // @Override // public void repaint(Rectangle r) { // } // public void repaint() { // } @Override protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { System.out.println(propertyName); // // Strings get interned... // if (propertyName == "text" // || propertyName == "labelFor" // || propertyName == "displayedMnemonic" // || ((propertyName == "font" || propertyName == "foreground") // && oldValue != newValue // && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { // // super.firePropertyChange(propertyName, oldValue, newValue); // } } @Override public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } } protected static Point2D getPointAt(double radius, double angle) { double x = Math.round(radius / 2d); double y = Math.round(radius / 2d); double rads = Math.toRadians(-angle); double fullLength = Math.round((radius / 2d)); double xPosy = (Math.cos(rads) * fullLength); double yPosy = (Math.sin(rads) * fullLength); return new Point2D.Double(xPosy, yPosy); } public class Car extends JPanel { private double direction; private double speed; private BufferedImage background; public Car() { setOpaque(false); direction = Math.random() * 360; speed = 5 + (Math.random() * 10); int image = 1 + (int) Math.round(Math.random() * 5); try { String name = "/Car0" + image + ".png"; background = ImageIO.read(getClass().getResource(name)); } catch (IOException ex) { ex.printStackTrace(); } setSize(getPreferredSize()); // setBorder(new LineBorder(Color.RED)); } public void setDirection(double direction) { this.direction = direction; revalidate(); repaint(); } public double getDirection() { return direction; } public void move() { Point at = getLocation(); at.x += (int) (speed * Math.cos(Math.toRadians(-direction))); at.y += (int) (speed * Math.sin(Math.toRadians(-direction))); setLocation(at); } @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); if (background != null) { double radian = Math.toRadians(direction); double sin = Math.abs(Math.sin(radian)), cos = Math.abs(Math.cos(radian)); int w = background.getWidth(), h = background.getHeight(); int neww = (int) Math.floor(w * cos + h * sin); int newh = (int) Math.floor(h * cos + w * sin); size = new Dimension(neww, newh); } return size; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); int x = (getWidth() - background.getWidth()) / 2; int y = (getHeight() - background.getHeight()) / 2; g2d.rotate(Math.toRadians(-(direction + 180)), getWidth() / 2, getHeight() / 2); g2d.drawImage(background, x, y, this); g2d.dispose(); // Debug graphics... // int cx = getWidth() / 2; // int cy = getHeight() / 2; // // g2d = (Graphics2D) g.create(); // g2d.setColor(Color.BLUE); // double radius = Math.min(getWidth(), getHeight()); // Point2D pointAt = getPointAt(radius, direction); // g2d.draw(new Ellipse2D.Double(cx - (radius / 2d), cy - (radius / 2d), radius, radius)); // // double xo = cx; // double yo = cy; // double xPos = cx + pointAt.getX(); // double yPos = cy + pointAt.getY(); // // g2d.draw(new Line2D.Double(xo, yo, xPos, yPos)); // g2d.draw(new Ellipse2D.Double(xPos - 2, yPos - 2, 4, 4)); // g2d.dispose(); } @Override public void invalidate() { } @Override public void validate() { } @Override public void revalidate() { } @Override public void repaint(long tm, int x, int y, int width, int height) { } @Override public void repaint(Rectangle r) { } @Override public void repaint() { } @Override protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // System.out.println(propertyName); // // Strings get interned... // if (propertyName == "text" // || propertyName == "labelFor" // || propertyName == "displayedMnemonic" // || ((propertyName == "font" || propertyName == "foreground") // && oldValue != newValue // && getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey) != null)) { // // super.firePropertyChange(propertyName, oldValue, newValue); // } } @Override public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { } } } 

ps – 我应该加1-我的10个月大的爱 – 它提醒了我跑步的工作:P