2013-03-19 4 views
6

Sto creando una transizione animata di diapositive in Java, ed è irregolare sul mio modello attuale MacBook Pro e sul mio vecchio iMac, su Java 6, 7 e 8.Come posso rendere più fluida l'animazione di Java Swing

Cosa posso fare per rendere questa animazione più uniforme all'utente su 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()); 
     } 
    } 
} 
+0

Il valore per isDoubleBuffered() nella JPanel è vero –

+0

1) Utilizzare un BufferedImage per l'immagine di sfondo. 2) Limita quanto ridicherai usando un sovraccarico '' repaint (...) 'che ridipinge solo un'area limitata della GUI, quanto basta per i tuoi scopi. 3) Gioca con il tuo frame rate per vedere cosa funziona meglio. 4) Sperimenta con la libreria di animazione/GUI non Swing come Slick che può essere meglio ottimizzata per l'animazione. –

+1

Riduci le dimensioni dell'area che stai pitturando – MadProgrammer

risposta

11

Molto dipende da cosa si vuole raggiungere.

Ricordate, l'animazione è l'illusione del movimento ...

ho cambiato

  • il framesPerSecond-60 che sembra aver aiutato.
  • Ridotto l'altezza complessiva dell'area di stampa
  • Calcolato l'area di modifica e semplice chiamato repaint(Rectangle) passando solo in quell'area che è stata modificata.

L'altro problema è che l'animazione ha una vasta area da coprire in un periodo di tempo molto breve. Aumentando la quantità di tempo anche renderlo più morbido (o ridurre la larghezza e/o altezza)

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

Alternativly, si potrebbe generare ti propria backup buffer e aggiornarlo ...

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

L' il problema principale che hai è in sostanza l'area che stai cercando di dipingere è troppo grande per il tempo in cui vuoi dipingerlo.

Un'altra alternativa

si potrebbe creare una BufferedImage che rappresenta la barra di avanzamento e muoversi come gli aggiornamenti sullo stato di avanzamento. Questo aggiornamento crea un'immagine compatibile tamponata, che dovrebbe renderlo più veloce al renderer

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

Grazie, è stato utile. Ho notato che quando passo da un desktop Mac OS X, la transizione scorrevole è molto più fluida, e la sto usando come punto di riferimento di ciò che è possibile. Sfortunatamente Java sul Mac non sembra avere accesso alle funzionalità di basso livello del computer. –

+0

Non molto ha accesso all'accelerazione a basso livello su Mac, questo è il reclamo principale di Adobe con Flash. – MadProgrammer