java什么比较重:Canvas或paintComponent()?

有人能告诉我在JPanel上绘制图形应该用什么: Canvas ,或者只是在paintComponent()绘制所有内容? 我每秒约30次绘制数百个小图像,我想知道哪一个是最轻量级的,我应该在什么条件下使用它们? 谢谢。

问题相当广泛,背景相当渺茫。

考虑看看旋转多个图像导致闪烁。 例如,Java Graphics2D和Swing动画运行速度极慢 。

第一种方法是使用paintComponent以25fps(或尽可能接近)渲染多达10,000个旋转图像。

第二个实际上可以动画最多4,500个基于组件的图像

以这种速度有效地绘画的能力只是整体情况的一个考虑因素

更新

使用JPanelCanvas之间的主要区别在于渲染算法的不同之处。

有了JPanel你仍然受到重画经理的支配。 这种方法通常称为“被动渲染”。 也就是说,经理负责确定应该绘制什么和何时绘制。 当重绘管理器决定需要绘制某些内容时,绘图是临时完成的,例如因为某些OS事件已请求应更新部分或整个屏幕。

从技术上讲,您无法控制此过程,只能简单地请求重绘应该发生。 您可能偶尔也会遇到一些延迟,因为系统已经插入并强制更新

Canvas为您提供了一个BufferStrategy ,它与底层渲染管道关系更紧密,可能会更快。

有了这个,你就负责安排重绘。 这通常称为“主动渲染”。

你仍然可以通过这种方法与操作系统联系……

无论哪种方式,除非你的更新/渲染管道得到很好的优化,你仍然会遇到很多问题,实际上可以通过使用JPanel而不是Canvas获得更好的性能

就个人而言,如果您不确定或之前没有做过这样的事情,我会从JPanel开始。 一般来说,处理起来会稍微简单一些。

我更改了链接的示例以维护FPS计数器…

MiniFig

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class ZombieLand { protected static final Random RND = new Random(); private static BufferedImage zombie; private static int fps = 25; public static void main(String[] args) { new ZombieLand(); } public ZombieLand() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } try { zombie = ImageIO.read(getClass().getResource("/MiniFig.png")); } catch (IOException ex) { ex.printStackTrace(); } final ZombiePane zombiePane = new ZombiePane(); final JSlider slider = new JSlider(1, 10000); slider.setMajorTickSpacing(1000); slider.setMinorTickSpacing(100); slider.setPaintTicks(true); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); zombiePane.setZombies(slider.getValue()); } }); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(zombiePane); frame.add(slider, BorderLayout.SOUTH); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { slider.setValue(10000); } }); } }); } public static class ZombiePane extends JPanel { private List sprites; protected static final Object SPRITE_LOCK = new Object(); private int desiredCount = 1; public ZombiePane() { sprites = new ArrayList<>(25); sprites.add(new ZombieSprite()); Thread t = new Thread(new GameLoop()); t.setDaemon(false); t.start(); Font font = getFont(); setFont(font.deriveFont(Font.BOLD, 48f)); } public void setZombies(int count) { desiredCount = count; } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); synchronized (SPRITE_LOCK) { for (ZombieSprite sprite : sprites) { sprite.paint(g2d); } } String text = Integer.toString(sprites.size()); FontMetrics fm = g2d.getFontMetrics(); g2d.drawString(text, getWidth() - fm.stringWidth(text), getHeight() - fm.getHeight() + fm.getAscent()); text = Integer.toString(fps); g2d.drawString(text, 0, getHeight() - fm.getHeight() + fm.getAscent()); g2d.dispose(); } protected void cycle() { synchronized (SPRITE_LOCK) { if (desiredCount != sprites.size()) { int count = 0; int fill = 100; while (sprites.size() > desiredCount && count < fill) { sprites.remove(0); count++; } count = 0; while (sprites.size() < desiredCount && count < fill) { sprites.add(new ZombieSprite()); count++; } } for (ZombieSprite sprite : sprites) { sprite.update(getWidth(), getHeight()); } } } public static class ZombieSprite { private Point motionDelta; private double rotationDelta; private Point location; private double angle; public ZombieSprite() { motionDelta = new Point(); motionDelta.x = (int) ((Math.random() * 3) + 1); motionDelta.y = (int) ((Math.random() * 3) + 1); if (Math.random() > 0.5) { motionDelta.x *= -1; } if (Math.random() > 0.5) { motionDelta.y *= -1; } rotationDelta = (int) ((Math.random() * 9) + 1); if (Math.random() > 0.5) { rotationDelta *= -1; } } public void paint(Graphics2D g2d) { if (location != null) { Graphics2D g = (Graphics2D) g2d.create(); AffineTransform at = new AffineTransform(); at.translate(location.x, location.y); at.rotate(Math.toRadians(angle), zombie.getWidth() / 2, zombie.getHeight() / 2); g.setTransform(at); g.drawImage(zombie, 0, 0, null); g.dispose(); } } public void update(int width, int height) { if (location == null) { angle = (Math.random() * 360d); location = new Point(); location.x = (int) (Math.random() * (width - zombie.getWidth())); location.y = (int) (Math.random() * (height - zombie.getHeight())); } else { angle += rotationDelta; location.x += motionDelta.x; location.y += motionDelta.y; if (location.x < 0) { location.x = 0; motionDelta.x *= -1; } else if (location.x + zombie.getWidth() > width) { location.x = width - zombie.getWidth(); motionDelta.x *= -1; } if (location.y < 0) { location.y = 0; motionDelta.y *= -1; } else if (location.y + zombie.getHeight() > height) { location.y = height - zombie.getHeight(); motionDelta.y *= -1; } } } } public class GameLoop implements Runnable { private long last; private long start; private int wait; private boolean keepRunning = true; public void run() { // Calculate the optimal/maximum delay time // This is converted to nanos so it can be // used to calculate the actual delay... long millisPerSecond = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS); long optimalDelay = Math.round(millisPerSecond / 25); optimalDelay = TimeUnit.MILLISECONDS.toNanos(optimalDelay); // Last start of a "second" loop long loop = System.nanoTime(); int frameCount = 0; // While gaming... while (keepRunning) { // Start of this cycle... long now = System.nanoTime(); // Update the state and render the // current frame... cycle(); repaint(); // How long did that update take?? long timeTaken = System.nanoTime(); long delta = timeTaken - now; // Subtract the delay from the maximum delay long delay = optimalDelay - delta; if (delay > 0) { try { // Sleep expects milliseconds... delay = TimeUnit.NANOSECONDS.toMillis(delay); Thread.sleep(delay); } catch (InterruptedException ex) { ex.printStackTrace(); } } // Calculate if we've being running for a second yet... long loopDelay = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - loop); // If the loop has been cycling for a second... if (loopDelay >= 1) { // Reset the loop time loop = System.nanoTime(); System.out.println("FPS = " + frameCount); fps = frameCount; frameCount = 0; } else { // Add another frame to the pile... frameCount++; } } } } } }