repaint() paints slower than paintComponent()?

2019-09-16 08:24发布

I am painting vehicle objects that I defined using the paintComponent(). Because the vehicles can move, I implement ActionListener and set a Timer() to trigger.

As a result, my vehicles can move. But it is kind of "shaking". When I keep resizing the window to call the paintComponent(), the movement becomes smooth. When I do not resize the window (not calling paintComponent), it gets skaking again. Why? How to fix it?

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
    }


} 

1条回答
等我变得足够好
2楼-- · 2019-09-16 08:44

Start by taking a look at Painting in AWT and Swing. Remember, repaint is only a suggest made to the RepaintManager, the RepaintManager may choose to consolidate multiple repaint calls into a smaller number of actual paint events.

Make sure you are calling super.paintComponent, otherwise you will end up with no end of strange paint artifacts.

Don't, directly or indirectly, modify the state of the component or ant other components from within any paint method, this will result in a new repaint request been made, which could lead to a cycle of paint events which could consume your CPU cycles. This means, don't call timer.start()!

Without a runable example to go by, I hobbled this together. Now this is animating 10, 000 individual Vehicles (rectangles), so it's massively over kill, but it should provide the point...

Noise

(the gif is only running at 7fps, not your 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);
        }

    }

}

You could also take a look at this example which renders upwards of 4500 images in random directions and demonstrates some optimisation techniques.

You can also take a look at this example which is capable of animating both in direction and rotation, upwards of 10, 000 images

查看更多
登录 后发表回答