Search code examples
javaswingrepaintevent-dispatch-thread

repaint for javax not work


Here is my Code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class Main {
// code main
public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(460, 500);
    frame.setTitle("Circles generator");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            frame.setVisible(true);

        }

    });

    String input = JOptionPane.showInputDialog("Enter n:");
    CustomComponents0 component = new CustomComponents0();
    frame.add(component);
    frame.getContentPane().validate();
        System.out.println("work before");
    frame.getContentPane().repaint();
        System.out.println("work");

    frame.getContentPane().repaint();
        System.out.println("work after");

}

// why is not JComponent
static class CustomComponents0 extends JLabel {

    private static final long serialVersionUID = 1L;

    @Override
    public Dimension getMinimumSize() {
        return new Dimension(200, 100);

    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 200);

    }

    @Override
    public void paintComponent(Graphics g) {
        System.out.println("paint");
        int margin = 10;
        Dimension dim = getSize();
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);

    }

}

here is the code and I want run repaint before work and run repaint after work. When I run it, it should print

work before
paint
work
paint
work after

but there are only one paint and it's after work, Why is that happen? How can I fix that?

Thanks.


Solution

  • You must construct and manipulate Swing GUI objects only on the event dispatch thread. Because your program is incorrectly synchronized, any result is possible. It will depend in part on how far the initial thread gets before starting the EventQueue. Moreover, println() itself may be synchronized, and "events being posted to the EventQueue can be coalesced."

    The variation below reliably shows the following output because events are dispatched "In the same order as they are enqueued." Note in particular how the calls to repaint() are coalesced. While this approach is illustrative, it is needlessly cumbersome for your likely goal. Instead, use javax.swing.Timer to pace animation as shown here.

    Console:

    paint
    work before
    work
    work after
    paint
    

    Code:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
    /** @see https://stackoverflow.com/a/44212328/230513 */
    public class Main {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> {
                JFrame frame = new JFrame();
                frame.setTitle("Circles generator");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                CustomComponent component = new CustomComponent();
                frame.add(component);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                EventQueue.invokeLater(() -> { System.out.println("work before"); });
                EventQueue.invokeLater(() -> { frame.repaint(); });
                EventQueue.invokeLater(() -> { System.out.println("work"); });
                EventQueue.invokeLater(() -> { frame.repaint(); });
                EventQueue.invokeLater(() -> { System.out.println("work after"); });
            });
        }
    
        static class CustomComponent extends JLabel {
    
            private static final int N = 10;
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 200);
            }
    
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                System.out.println("paint");
                g.setColor(Color.red);
                g.fillRect(N, N, getWidth() - N * 2, getHeight() - N * 2);
            }
        }
    }