【问题标题】:repaint() paints slower than paintComponent()?repaint() 绘制比paintComponent() 慢?
【发布时间】:2015-05-03 16:54:00
【问题描述】:

我正在绘制使用paintComponent() 定义的车辆对象。 因为车辆可以移动,所以我实现了 ActionListener 并设置了一个 Timer() 来触发。

因此,我的车辆可以移动。但这有点“颤抖”。当我不断调整窗口大小以调用paintComponent() 时,移动变得平滑。当我不调整窗口大小(不调用paintComponent)时,它会再次滑动。为什么?如何解决?

public class VehiclesComponent extends JComponent implements ActionListener{
    private Vehicle[] vehicles;
    private Timer timer;

    public VehiclesComponent(int n){
        vehicles = Vehicle.generateVehicle(n);
        timer = new Timer(5,this);
    } 

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;

        for (int i=0; i<vehicles.length; i++) {
            vehicles[i].draw(g2);
        }

        // may change later
        timer.start();
    }

    @Override
    public void actionPerformed(ActionEvent e){

        //check collision in here
        for (Vehicle v : vehicles) {
            if (Vehicle.intersectsOther(v, vehicles)) {
                v.collisionSideEffect();
            }
        }

        //move all in here

        for (Vehicle v : vehicles ) {
            v.move();
        }

        repaint(); 
        //?? repaint slower than paintComponent
    }


} 

【问题讨论】:

  • public void paintComponent(Graphics g){ .. 就像这里提到的每天应该是public void paintComponent(Graphics g){ super.paintComponent(g); .. 并且1)将// may change later timer.start();移到我们无法控制的方法之外以及它被调用了多少次。 2) 使用逻辑一致的形式缩进代码行和块。缩进是为了让代码的流程更容易理解!
  • 它应该保持protected
  • 首先看一下Painting in AWT and Swing. repaint` 向负责在事件队列上调度绘制事件的RepaintManager 发出请求。为了提高性能,repaint 请求可以合并为单个(或更少数量的)绘制事件。
  • 生成的绘制事件数repaint的次数...
  • 考虑提供一个runnable example 来证明您的问题。这不是代码转储,而是您正在做的事情的一个示例,它突出了您遇到的问题。这将导致更少的混乱和更好的响应

标签: java swing awt actionlistener paintcomponent


【解决方案1】:

首先查看Painting in AWT and Swing。请记住,repaint 只是对RepaintManager 的建议,RepaintManager 可以选择将多个repaint 调用合并为较少数量的实际绘制事件。

确保您拨打的是super.paintComponent,否则您将得到无穷无尽的奇怪油漆工件。

不要在任何绘制方法中直接或间接修改组件或其他组件的状态,这将导致发出新的repaint 请求,这可能导致绘制事件循环可能会消耗您的 CPU 周期。这意味着,不要打电话给timer.start()

没有一个可运行的例子,我把它拖到一起。现在这是为 10, 000 个人 Vehicles(矩形)设置动画,所以它的杀伤力太大了​​,但它应该提供重点......

(gif 仅以 7fps 运行,而不是您的 200fps)

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.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new VehiclesComponent(10000));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class VehiclesComponent extends JComponent implements ActionListener {

        private Vehicle[] vehicles;
        private Timer timer;

        public VehiclesComponent(int n) {
            vehicles = Vehicle.generateVehicle(n, getPreferredSize());
            timer = new Timer(5, this);

            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;

            for (int i = 0; i < vehicles.length; i++) {
                vehicles[i].draw(g2);
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            //check collision in here
//          for (Vehicle v : vehicles) {
//              if (Vehicle.intersectsOther(v, vehicles)) {
//                  v.collisionSideEffect();
//              }
//          }

        //move all in here
            for (Vehicle v : vehicles) {
                v.move(this.getSize());
            }

            repaint();
            //?? repaint slower than paintComponent
        }

    }

    public static class Vehicle {

        protected static final int SIZE = 5;
        protected static final Color[] COLORS = new Color[]{
            Color.BLACK,
            Color.BLUE,
            Color.CYAN,
            Color.DARK_GRAY,
            Color.GREEN,
            Color.MAGENTA,
            Color.ORANGE,
            Color.PINK,
            Color.RED,
            Color.WHITE,
            Color.YELLOW
        };

        private int x = 0;
        private int y = 0;

        private int xDelta;
        private int yDelta;

        private Shape car;
        private Color color;

        public static Vehicle[] generateVehicle(int count, Dimension bounds) {

            Vehicle[] vehicles = new Vehicle[count];
            for (int index = 0; index < vehicles.length; index++) {
                vehicles[index] = new Vehicle(bounds);
            }

            return vehicles;

        } 

        public Vehicle(Dimension size) {

            x = (int)(Math.random() * (size.width - SIZE));
            y = (int)(Math.random() * (size.height - SIZE));

            xDelta = (int)(Math.random() * 3) + 1;
            yDelta = (int)(Math.random() * 3) + 1;
            car = new Rectangle(SIZE, SIZE);

            color = COLORS[(int)(Math.random() * COLORS.length)];

        }

        public void move(Dimension size) {
            x += xDelta;
            y += yDelta;

            if (x < 0) {
                x = 0;
                xDelta *= -1;
            } else if (x + SIZE > size.width) {
                x = size.width - SIZE;
                xDelta *= -1;
            }
            if (y < 0) {
                y = 0;
                yDelta *= -1;
            } else if (y + SIZE > size.height) {
                y = size.height - SIZE;
                yDelta *= -1;
            }

        }

        public void draw(Graphics2D g2) {
            g2.translate(x, y);
            g2.setColor(color);
            g2.fill(car);
            g2.translate(-x, -y);
        }

    }

}

您还可以查看this example,它可以在随机方向上渲染超过 4500 张图像并演示一些优化技术。

您还可以查看this example,它能够在方向和旋转方面进行动画处理,多达 10,000 张图像

【讨论】:

  • “所以它的杀伤力太大了​​” 没有比杀伤力更大的杀伤力了。 :)
  • 虽然我看到了对现有代码只做“少量”修改的意义(为了提供答案,而不仅仅是告诉提问者他正在使用错误的方法),人们可能应该强调不应滥用Components 作为精灵。一个组件就是一个组件,一个精灵就是一个精灵。绘制 10000 个简单的矩形(在一个自己的组件中,在paintComponent 方法中)将可能超过 7 fps...
  • @Marco13 前两点是,调用super.paintComponent,不要在paintComponent方法中调用timer.start。 gif 动画为 7fps,因此它看起来有点“交错”的原因,该示例能够以 200fps 的速度最大化。前两点将根据 OP 提供的内容有所不同。 OP 没有提供任何关于他们正在画什么或如何画的信息,因此不可能提供更多细节。如果您愿意,我可以发布一个示例的链接,该示例也可以为 4000-5000 个随机图像设置动画...
  • @Marco13 如果我正确理解评论,就我所知,OP 是作为“油漆”表面的单个组件,然后以其他方式渲染车辆(因此draw) 方法。我希望在某个时候鼓励他们提供更多信息......
猜你喜欢
  • 1970-01-01
  • 2020-05-22
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 2015-03-30
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
相关资源
最近更新 更多