Search code examples
javaswingpaintcomponent

paintComponent not working


this may be a silly question but How do i call the paintComponent? Its not displaying the object at all. its within the, public class Ball extends JPanel implements Runnable.

public class Balls {

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

    public Balls() {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Balls!");
                frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
                frame.add(new ballAdder());
                frame.setSize(1000, 1000);
                frame.setVisible(true);

            }
        });
    }

    public class ballAdder extends JPanel {

        public ballAdder() {
            add(new Ball(5, 5));

        }
    }

    public class Ball extends JPanel implements Runnable {

        public int x, y;
        public int speedx, speedy;
        public int width = 40, height = 40;

        public Ball(int x, int y) {
            this.x = x;
            this.y = y;
            new Thread(this).start();

        }

        public void move() {
            x += speedx;
            y += speedy;
            if (0 > x || x > 950) {
                speedx = -speedx;
            }
            if (0 > y || y > 950) {
                speedy = -speedy;
            }
            repaint();
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.BLACK);
            g.fillOval(x, y, width, height);
        }

        public void run() {
            while (true) {
                move();
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Solution

  • You should never call paintComponent (or paint) yourself. This is done by the RepaintManager.

    The problem you're actually having is that fact speedx and speedy or 0, meaning you balls never move...

    The other issue is that the ballAdder class is using a FlowLayout and you Ball class is not providing any details about it's preferred size, meaning that the Ball panel has a preferred size of 0x0.

    Review

    There is a significant issue with scalability with your design. Apart from the fact that you're going to find it difficult to add more then one ball to the UI because of layout issues...

    Each Ball has it's own thread. This means, the more balls you add, the more threads that are going to be running. This is going to have a steady drain on resources and effect the performance of your application.

    It would be better to provide a concept of a Drawable object which knew where it should be displayed within the concept it's container and could be painted from within the paintComponent. By utilising a single javax.swing.Timer it should be more capable of supporting a growing number of random balls.

    First Fix

    To fix you're first issue, you could do something like this...

    public class ballAdder extends JPanel {
        public ballAdder() {
            setLayout(new BorderLayout());
            add(new Ball(5, 5));
        }
    }
    

    The problem with this fix is that you're only ever going to be able to have a single Ball on the container, as it will want to occupy the maximum available space.

    You might want to have a read through Using Layout Managers for more details

    A (possible) better solution

    A (possible) better solution would be to use a single JPanel as a "ball pit" which maintained a reference to list of balls.

    You would then use the BallPitPane's paintComponent method to draw all the balls (in the balls list).

    Through the use of a single javax.swing.Timer, you could iterate through the balls list and update there positions (within the context of the BallPitPane

    IMHO, this easier then trying to fight with the layout managers or writing your own...

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Bounce {
    
        public static void main(String[] args) {
            new Bounce();
        }
    
        public Bounce() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new BallPitPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class BallPitPane extends JPanel {
    
            private List<Ball> balls;
            private Random rand;
    
            public BallPitPane() {
                rand = new Random(System.currentTimeMillis());
                balls = new ArrayList<>(25);
                Timer timer = new Timer(40, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (balls.isEmpty()) {
                            balls.add(new Ball(BallPitPane.this));
                        }
    
                        if (rand.nextBoolean()) {
                            balls.add(new Ball(BallPitPane.this));
                        }
    
                        for (Ball ball : balls) {
                            ball.move();
                        }
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                for (Ball ball : balls) {
                    ball.paint(g2d);
                }
                g2d.dispose();
            }
        }
    
        protected static int random(int min, int max) {
    
            return (int)Math.round(Math.random() * (max - min)) + min;
    
        }
    
        public static class Ball {
    
            public static final int WIDTH = 10;
            public static final int HEIGHT = 10;
    
            private int x;
            private int y;
    
            private int deltaX;
            private int deltaY;
    
            private Color color;
            private BallPitPane parent;
    
            public Ball(BallPitPane parent) {
                this.parent = parent;
                x = parent.getWidth() / 2;
                y = parent.getHeight() / 2;
    
                deltaX = random(-4, 4);
                deltaY = random(-4, 4);
    
                color = new Color(random(0, 255), random(0, 255), random(0, 255));
            }
    
            public void move() {
                x += deltaX;
                y += deltaY;
    
                if (x + WIDTH > parent.getWidth()) {
                    x = parent.getWidth() - WIDTH;
                    deltaX *= -1;
                } else if (x < 0) {
                    x = 0;
                    deltaX *= -1;
                }
                if (y + HEIGHT > parent.getHeight()) {
                    y = parent.getHeight() - HEIGHT;
                    deltaY *= -1;
                } else if (y < 0) {
                    y = 0;
                    deltaY *= -1;
                }
            }
    
            public Color getColor() {
                return color;
            }
    
            public void paint(Graphics2D g2d) {
    
                g2d.setColor(getColor());
                g2d.fillOval(x, y, WIDTH, HEIGHT);
                g2d.setColor(Color.BLACK);
                g2d.drawOval(x, y, WIDTH, HEIGHT);
    
            }        
        }    
    }