代码之家  ›  专栏  ›  技术社区  ›  Mattijs

Swing活动呈现效率或如何将活动呈现与gui小部件相结合

  •  3
  • Mattijs  · 技术社区  · 14 年前

    从一个 previous question ,我一直在寻找在Java中将活动呈现与文本字段相结合的最佳方法。我尝试了几个选项,使用BufferStrategy、VolatileImage或覆盖标准AWT中的update()和paint(),但最终使用了Swing。

    我在这里发布了当前的状况,以防有人碰巧根据我的代码示例有了新的见解,也许其他正在开发类似应用程序的人可能会从我的发现中受益。

    • 在仅在必要时更新的背景缓冲区上渲染动画对象
    • 在渲染结果顶部使用文本字段

    下面是在stackoverflower的帮助下开发的演示应用程序的代码 trashgod
    两个注意事项:

    2) 将BuffereImage绘制到屏幕的效率在很大程度上取决于平台。Mac实现似乎不支持正确的硬件加速,这使得将背景图像重新绘制到输出窗口成为一项乏味的任务,当然这取决于窗口的大小。



    1920 x 1100:5毫秒,35-40%

    Windows XP操作系统:

    1920 x 1100:0.05毫秒,0%


    屏幕大小:绘制帧的平均时间,应用程序的CPU使用率。

    据我所知,下面的代码是实现我的目标最有效的方法。任何新的见解,优化或测试结果是非常欢迎的!

    当做, 马提斯

    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.GraphicsDevice;
    import java.awt.GraphicsEnvironment;
    import java.awt.GridLayout;
    import java.awt.Rectangle;
    import java.awt.Transparency;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.image.BufferedImage;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.Timer;
    
    public class SwingTest extends JPanel implements 
     ActionListener, 
     Runnable 
    {
     private static final long serialVersionUID = 1L;
    
     private BufferedImage backgroundBuffer;
        private boolean repaintbackground = true;
    
        private static final int initWidth = 640;
        private static final int initHeight = 480;
        private static final int radius = 25;
        private final Timer t = new Timer(20, this);
        private final Rectangle rect = new Rectangle(); 
    
        private long totalTime = 0;
        private int frames = 0;
        private long avgTime = 0;
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new SwingTest());
        }
    
        public SwingTest() {
            super(true);
            this.setPreferredSize(new Dimension(initWidth, initHeight));
            this.setLayout(null);
            this.setOpaque(false);
            this.addMouseListener(new MouseHandler());
        }
    
        @Override
        public void run() {
            JFrame f = new JFrame("SwingTest");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.addComponentListener(new ResizeHandler());
    
    /*      This extra Panel with GridLayout is necessary to make sure 
       our content panel is properly resized with the window.*/
            JPanel p = new JPanel(new GridLayout()); 
            p.add(this);
            f.add(p);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
    
            createBuffer();     
            t.start();
        }    
    
        @Override
        public void actionPerformed(ActionEvent e) {
            this.repaint();
        }    
    
        @Override
        protected void paintComponent(Graphics g) {
         long start = System.nanoTime();
         super.paintComponent(g);
    
         if (backgroundBuffer == null) createBuffer();
         if (repaintbackground) {
    
    /*   Repainting the background may require complex rendering operations, 
       so we don't want to do this every frame.*/       
          repaintBackground(backgroundBuffer);
                repaintbackground = false;
         }
    
    /*  Repainting the pre-rendered background buffer every frame
           seems unavoidable. Previous attempts to keep track of the 
           invalidated area and repaint only that part of the background buffer 
           image have failed. */
         g.drawImage(backgroundBuffer, 0, 0, null);
         repaintBall(g, backgroundBuffer, this.getWidth(), this.getHeight());
         repaintDrawTime(g, System.nanoTime() - start);
        }
    
        void repaintBackground(BufferedImage buffer) {    
         Graphics2D g = buffer.createGraphics();
      int width = buffer.getWidth();
      int height = buffer.getHeight();
    
      g.clearRect(0, 0, width, height);
      for (int i = 0; i < 100; i++) {
       g.setColor(new Color(0, 128, 0, 100));
       g.drawLine(width, height, (int)(Math.random() * (width - 1)), (int)(Math.random() * (height - 1)));
      }
        }
    
        void repaintBall(Graphics g, BufferedImage backBuffer, int width, int height) {
         double time = 2* Math.PI * (System.currentTimeMillis() % 3300) / 3300.;
            rect.setRect((int)(Math.sin(time) * width/3 + width/2 - radius), (int)(Math.cos(time) * height/3 + height/2) - radius, radius * 2, radius * 2);
    
            g.setColor(Color.BLUE);
            g.fillOval(rect.x, rect.y, rect.width, rect.height);
        }
    
        void repaintDrawTime(Graphics g, long frameTime) {
         if (frames == 32) {avgTime = totalTime/32; totalTime = 0; frames = 0;}
         else {totalTime += frameTime; ++frames; }
         g.setColor(Color.white);
         String s = String.valueOf(avgTime / 1000000d + " ms");
            g.drawString(s, 5, 16);
        }
    
        void createBuffer() {
            int width = this.getWidth();
            int height = this.getHeight();
    
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            backgroundBuffer = gc.createCompatibleImage(width, height, Transparency.OPAQUE);        
    
            repaintbackground = true;
        }    
    
        private class MouseHandler extends MouseAdapter {
    
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                JTextField field = new JTextField("test");
                Dimension d = field.getPreferredSize();
                field.setBounds(e.getX(), e.getY(), d.width, d.height);
                add(field);
            }
        }
    
        private class ResizeHandler extends ComponentAdapter {
    
         @Override
         public void componentResized(ComponentEvent e) {
          super.componentResized(e);
          System.out.println("Resized to " + getWidth() + " x " + getHeight());
          createBuffer();
         }    
        }
    }
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   Community Teyam    7 年前

    1. 您的 repaintDrawTime() 非常可读,但它是一个 micro-benchmark vagaries 主机操作系统的。我忍不住想知道XP的结果是否是该系统性能的产物 limited clock resolution

    2. 如果不使用空布局,就不需要额外的面板;的默认布局 JPanel FlowLayout ,和 f.add(this) 只需将其添加到框架默认值的中心 BorderLayout

    3. 重复的构造函数调用可能非常耗时。

      考虑更换

      g.setColor(new Color(0, 128, 0, 100));
      

      private static final Color color = new Color(0, 128, 0, 100);
      ...
      g.setColor(color);
      

      或者,一个简单的 color lookup table ,可能有用,例如。

      private final Queue<Color> clut = new LinkedList<Color>();
      
        2
  •  1
  •   michaelsnowden    11 年前

    void repaintBall(Graphics g, BufferedImage backBuffer, int width, int height) {
     double time = 2* Math.PI * (System.currentTimeMillis() % 3300) / 3300.;
        rect.setRect((int)(Math.sin(time) * width/3 + width/2 - radius), (int)(Math.cos(time) * height/3 + height/2) - radius, radius * 2, radius * 2);
    
        g.setColor(Color.BLUE);
        g.fillOval(rect.x, rect.y, rect.width, rect.height);
    }
    

    你好像从来没有在方法体中使用过。

    我能够将这个类的图形部分移植到我自己的JPanel构造函数类中,它大大改进了我的游戏的图形,但是我从不需要使用这样的方法,在这里我传入一个BufferedImage作为参数,但从不使用它。