¿Cómo puedo hacer una animación Java Swing más suave?

Estoy creando una transición de diapositivas animada en Java, y está rota en mi modelo actual MacBook Pro y en mi iMac de un año, en Java 6, 7 y 8.

¿Qué puedo hacer para que esta animación parezca más suave para el usuario en Mac OS X?

import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class ScratchSpace { public static void main(String[] args) { AnimatedPanel panel = new AnimatedPanel(); JFrame frame = new JFrame(); frame.setContentPane(panel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); panel.animate(); } public static class AnimatedPanel extends JPanel { private float progress = 0.0f; // a number between 0.0 and 1.0 public AnimatedPanel() { setPreferredSize(new Dimension(800, 600)); setOpaque(true); } public void animate() { final int animationTime = 1000; int framesPerSecond = 30; int delay = 1000 / framesPerSecond; final long start = System.currentTimeMillis(); final Timer timer = new Timer(delay, null); timer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final long now = System.currentTimeMillis(); final long elapsed = now - start; progress = (float) elapsed / animationTime; repaint(); if (elapsed >= animationTime) { timer.stop(); } } }); timer.start(); } @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; int width = getWidth(); int progressWidth = (int) (width * progress); g2d.setColor(Color.BLUE); g2d.fillRect(0, 0, progressWidth, getHeight()); g2d.setColor(Color.RED); g2d.fillRect(progressWidth, 0, width-progressWidth, getHeight()); } } } 

Mucho depende de qué es lo que finalmente quieres lograr.

Recuerde, la animación es la ilusión de movimiento …

Cambié

  • los framesPerSecond a 60 que parecen haber ayudado.
  • Se redujo la altura total del área imprimible
  • Se calculó el área de cambio y el simple llamado repaint(Rectangle) pasa solo en esa área que ha cambiado.

El otro problema es que tu animación tiene un área grande para cubrir en un período de tiempo muy corto. Aumentar la cantidad de tiempo también lo hará más suave (o reducirá el ancho y / o la altura)

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class ScratchSpace { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } AnimatedPanel panel = new AnimatedPanel(); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(panel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); panel.animate(); } }); } public static class AnimatedPanel extends JPanel { private float progress = 0.0f; // a number between 0.0 and 1.0 public AnimatedPanel() { setPreferredSize(new Dimension(800, 100)); } public void animate() { final int animationTime = 1000; int framesPerSecond = 60; int delay = 1000 / framesPerSecond; final long start = System.currentTimeMillis(); final Timer timer = new Timer(delay, null); timer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final long now = System.currentTimeMillis(); final long elapsed = now - start; int width = getWidth(); int height = getHeight(); int oldWidth = (int) (width * progress); progress = (float) elapsed / animationTime; int newWidth = (int) (width * progress); repaint(new Rectangle(oldWidth, 0, newWidth - oldWidth, height)); if (elapsed >= animationTime) { timer.stop(); } } }); timer.start(); } @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; int width = getWidth(); int progressWidth = (int) (width * progress); g2d.setColor(Color.BLUE); g2d.fillRect(0, 0, progressWidth, getHeight()); g2d.setColor(Color.RED); g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight()); } } } 

Alternativamente, podrías generar tu propio buffer de respaldo y actualizarlo …

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class ScratchSpace { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } AnimatedPanel panel = new AnimatedPanel(); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(panel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); panel.animate(); } }); } public static class AnimatedPanel extends JPanel { private float progress = 0.0f; // a number between 0.0 and 1.0 private BufferedImage buffer; public AnimatedPanel() { setPreferredSize(new Dimension(800, 600)); // setOpaque(true); } public void animate() { final int animationTime = 1000; int framesPerSecond = 60; int delay = 1000 / framesPerSecond; final long start = System.currentTimeMillis(); final Timer timer = new Timer(delay, null); timer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final long now = System.currentTimeMillis(); final long elapsed = now - start; int width = getWidth(); progress = (float) elapsed / animationTime; updateBuffer(); repaint(); if (elapsed >= animationTime) { timer.stop(); } } }); timer.start(); } @Override public void invalidate() { buffer = null; updateBuffer(); super.invalidate(); } protected void updateBuffer() { if (getWidth() > 0 && getHeight() > 0) { if (buffer == null) { buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); } Graphics2D g2d = buffer.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 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 width = getWidth(); int height = getHeight(); float progressWidth = width * progress; g2d.setColor(Color.BLUE); g2d.fill(new Rectangle2D.Float(0, 0, progressWidth, height)); g2d.setColor(Color.RED); g2d.fill(new Rectangle2D.Float(progressWidth, 0, width - progressWidth, height)); g2d.dispose(); } } @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (buffer != null) { g2d.drawImage(buffer, 0, 0, this); } // int width = getWidth(); // int progressWidth = (int) (width * progress); // g2d.setColor(Color.BLUE); // g2d.fillRect(0, 0, progressWidth, getHeight()); // g2d.setColor(Color.RED); // g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight()); } } } 

El principal problema que tienes es básicamente que el área que intentas pintar es grande para el momento en que deseas pintarla.

Otra alternativa

Puede crear una BufferedImage que represente la barra de progreso y mover mientras se actualiza el progreso. Esta actualización crea una imagen de búfer compatible, lo que debería hacer que sea más rápido para el renderizador

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class ScratchSpace { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } AnimatedPanel panel = new AnimatedPanel(); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(panel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); panel.animate(); } }); } public static class AnimatedPanel extends JPanel { private float progress = 0.0f; // a number between 0.0 and 1.0 private BufferedImage buffer; public AnimatedPanel() { setPreferredSize(new Dimension(800, 600)); // setOpaque(true); } public void animate() { final int animationTime = 1000; int framesPerSecond = 60; int delay = 1000 / framesPerSecond; final long start = System.currentTimeMillis(); final Timer timer = new Timer(delay, null); timer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final long now = System.currentTimeMillis(); final long elapsed = now - start; progress = (float) elapsed / animationTime; repaint(); if (elapsed >= animationTime) { timer.stop(); } } }); timer.start(); } @Override public void invalidate() { buffer = null; updateBuffer(); super.invalidate(); } protected void updateBuffer() { if (getWidth() > 0 && getHeight() > 0) { if (buffer == null) { GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); buffer = config.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT); buffer.coerceData(true); Graphics2D g2d = buffer.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 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 width = getWidth(); int height = getHeight(); g2d.setColor(Color.BLUE); g2d.fill(new Rectangle2D.Float(0, 0, width, height)); g2d.dispose(); } } } @Override protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; int width = getWidth(); int progressWidth = (int) (width * progress); int x = (progressWidth - width); System.out.println(progressWidth + "; " + x); // g2d.setColor(Color.BLUE); // g2d.fillRect(0, 0, progressWidth, getHeight()); g2d.setColor(Color.RED); g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight()); g2d.drawImage(buffer, x, 0, this); } } }