Home > Enterprise >  Why is the repaint() method not calling my paintComponent() method?
Why is the repaint() method not calling my paintComponent() method?

Time:11-24

I was trying to copy a repaint() and a paintComponent() method from a tutorial. After I copied the two methods my paintComponent did not get called and so the rectangle is not being showed. Here is my code:

public class Main {
    GameWindow gw;
    
    Main() {
        gw = new GameWindow();
    }
    
    void start() {
        gw.setWindow();
    }

    public static void main(String[] args) {
        new Main().start();
    }
}

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameWindow extends JPanel implements Runnable {
    final int ORIGINAL_TILE_SIZE = 16;
    final int SCALE = 3;
    final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;

    final int MAX_SCREEN_COLUMNS = 16;
    final int MAX_SCREEN_ROWS = 12;
    final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
    final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;

    Thread animation;

    void setWindow() {
        JFrame window = new JFrame();

        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setResizable(false);

        window.setTitle("Avontuur");
        window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
        window.getContentPane().setBackground(Color.black);

        ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
        window.setIconImage(icon.getImage());
        window.pack();

        window.setLocationRelativeTo(null);
        window.setVisible(true);

        startAnimation();
    }

    void startAnimation() {
        animation = new Thread(this);
        animation.start();
    }

    @Override
    public void run() {
        while (animation != null) {
            update();

            repaint();
        }
    }
    
    public void update() {
        
    }
    
    public void paintComponent(final Graphics g) {
        
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D)g;
        
        g2.setColor(Color.white);
        g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
        g2.dispose();
    }
}

I already tried some solutions from stackOverflow, but they did not work or they were not relevant to my problem. Now the code above is what I tried myself using the video, but after using a println in the method I saw it was not getting called. I expected it to work after watching the tutorial, but it didn't. Does anyone know how I can fix this? Thanks in advance!

CodePudding user response:

Swing is single threaded - never block the Event Dispatching Thread with long running or blocking operations

Swing is NOT thread safe - never update the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.

See Concurrency in Swing for more details.

Swing makes use of a passive rendering engine - you don't control the painting process and you need to work within in it's intended design/workflow, see Painting in AWT and Swing and Performing Custom Painting for more details.

So, what's the solution? In it's simplest form, use a Swing Timer, for example...

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 javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new GamePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GamePane extends JPanel {
        final int ORIGINAL_TILE_SIZE = 16;
        final int SCALE = 3;
        final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;

        final int MAX_SCREEN_COLUMNS = 16;
        final int MAX_SCREEN_ROWS = 12;
        final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
        final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;

        private Timer timer;

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

        @Override
        public void addNotify() {
            super.addNotify();
            if (timer != null) {
                timer.stop();
            }

            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    update();
                }
            });
            timer.start();
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            if (timer != null) {
                timer.stop();
            }

        }

        public void update() {
            System.out.println("Updatey update");
            repaint();
        }

        public void paintComponent(final Graphics g) {
            super.paintComponent(g);

            System.out.println("Painty paint paint");

            Graphics2D g2 = (Graphics2D) g.create();

            g2.setColor(Color.white);
            g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
            g2.dispose();
        }
    }
}

You should now see a bunch of text been printed to the console.

Also - you might not, I've made some structural changes to your code, there is not reason for a JPanel based class to create it's own window (or if you really wanted to do this, I'd create a static method to do it, but then I'd be questioning why).

Oh, and also what DontKnowMuchBut Getting Better said in the comments!

CodePudding user response:

Here is the specific rectangle problem with your code, I will not mention other things because is not in the question, you are not adding GameWindow itself as component of your JFrame, and you are not calling your paintComponent method anywhere, I put it on update and it worked fine, but I'm afraid that your code will block and you will not able to move the window and interact with it since you are always calling update/repaint over and over again inside your while statement.

edit.: As the guy in the comment said, we must not call paintcomponent directly, so I fixed.

void setWindow() {
    JFrame window = new JFrame();

    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setResizable(false);

    window.setTitle("Avontuur");
    window.add(GameWindow.this);
    window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
    window.getContentPane().setBackground(Color.black);

    ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
    window.setIconImage(icon.getImage());
    window.pack();

    window.setLocationRelativeTo(null);
    window.setVisible(true);

    startAnimation();
}
  • Related