2013-03-19 181 views
6

我在Java中創建了一個動畫幻燈片切換,它在我當前的型號MacBook Pro和我一歲的iMac上,在Java 6,7和8上都不穩定。我如何使Java Swing動畫更流暢

我該怎麼做才能使此動畫在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

爲的isDoubleBuffered()的值在JPanel的是真實的 – 2013-03-19 22:07:27

+0

1)使用一個BufferedImage的背景圖像。 2)通過使用「repaint(...)'重載來限制重繪次數,該重載只重繪GUI的一個有限區域,僅用於您的目的。 3)玩你的幀率,看看什麼效果最好。 4)使用非Swing動畫/ GUI庫(如Slick)進行實驗,這可能會更好地針對動畫進行優化。 – 2013-03-19 22:23:36

+1

縮小您正在繪製的區域的尺寸 – MadProgrammer 2013-03-19 22:26:14

回答

11

很大程度上取決於您最終想達到的目標。

記住,動畫是運動的錯覺......

我改變

  • framesPerSecond60這似乎已經幫助。
  • 減小可打印區域的總高度
  • 計算改變的面積並簡單地調用repaint(Rectangle)只傳入已更改的區域。

另一個問題是您的動畫在很短的時間內有很大的覆蓋面積。增加的時間量也將使其更流暢(或減少寬度和/或高度)

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,你可以生成你自己的備份緩衝區和更新...

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

的你所擁有的主要問題基本上就是你想要繪製的區域大到你想要繪製的區域。

另一個或者

您可以創建一個BufferedImage表示進度條和移動的最新進展。此更新將創建一個兼容的緩衝圖像,這將使我們更快地呈示

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

謝謝,這是一些有用的東西。我注意到,當我在Mac OS X桌面之間切換時,滑動轉換更平滑,我將其用作可能的基準。不幸的是,Mac上的Java似乎無法訪問計算機的低級功能。 – 2013-03-19 22:45:22

+0

在Mac上沒有多少訪問低級加速的功能,這是Adobe使用Flash的主要抱怨。 – MadProgrammer 2013-03-19 22:46:25