Search code examples
javaswingjbuttonpaintjcomponent

Drawing a shape by clicking a JButton


I am currently trying to draw a figure by clicking a button. My problems occurs when I click the button and it does not show up in the panel, but I know it's drawing because it runs through the loop.

The drawing will occur when I request the panel to draw it inside the constructor, but not inside the button, inside the constructor

If i put the code in the method "stuff()" inside the constructor it will draw everything just fine.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainFrame{
    public static void main(String[]args){
        MainFrame f = new MainFrame();
    }
public JFrame frame = new JFrame();
public JPanel panel = new JPanel(new BorderLayout());
public MainFrame(){
    JButton button1 = new JButton("Shweet Button");
    button1.setBounds(185, 10, 130, 20);
    frame.setBounds(1680/4,1050/4,500, 500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.add(panel);
    panel.setBackground(Color.black);
    frame.setVisible(true);
    frame.getContentPane().setLayout(null);
    frame.add(button1);
    button1.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            stuff();
        }
    });
}
public void stuff(){
    for(int i = 0;i<1000;i++){
        panel.add(new paintComponent());
        panel.setBackground(Color.black);
        frame.repaint();
        try {
            Thread.sleep(80);
        } catch (InterruptedException e1){e1.printStackTrace();}
    }
}
static class paintComponent extends JComponent{
    public int options;
    public void paint(Graphics g){
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(Color.white);

        if(options == 0){
            options = 1;
            g2.drawOval(50, (JFrame.HEIGHT/2)+100, 50, 50);
            g2.drawOval(60, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.fillOval(60, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.drawOval(80, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.fillOval(80, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.drawArc(65, (JFrame.HEIGHT/2)+130, 100, 0, 140, 30);
            g2.drawLine(75, (JFrame.HEIGHT/2)+150, 75, (JFrame.HEIGHT/2)+220);
            g2.drawLine(75, (JFrame.HEIGHT/2)+180, 100, (JFrame.HEIGHT/2)+160);
            g2.drawLine(75, (JFrame.HEIGHT/2)+180, 65, (JFrame.HEIGHT/2)+210);
            g2.drawLine(75, (JFrame.HEIGHT/2)+220, 50, (JFrame.HEIGHT/2)+260);
            g2.drawLine(75, (JFrame.HEIGHT/2)+220, 100, (JFrame.HEIGHT/2)+260);
        }else if(options == 1){
            options = 0;
            g2.drawOval(50, (JFrame.HEIGHT/2)+100, 50, 50);
            g2.drawOval(60, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.fillOval(60, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.drawOval(80, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.fillOval(80, (JFrame.HEIGHT/2)+110, 8, 8);
            g2.drawArc(65, (JFrame.HEIGHT/2)+130, 100, 0, 140, 30);
            g2.drawLine(75, (JFrame.HEIGHT/2)+150, 75, (JFrame.HEIGHT/2)+220);
            g2.drawLine(75, (JFrame.HEIGHT/2)+180, 100, (JFrame.HEIGHT/2)+180);
            g2.drawLine(75, (JFrame.HEIGHT/2)+180, 65, (JFrame.HEIGHT/2)+210);
            g2.drawLine(75, (JFrame.HEIGHT/2)+220, 50, (JFrame.HEIGHT/2)+260);
            g2.drawLine(75, (JFrame.HEIGHT/2)+220, 100, (JFrame.HEIGHT/2)+260);
        }
    }
}
}

Solution

  • You are blocking the Event Dispatching Thread, which is responsible for, amongst other things, processing paint requests.

    You should never do anything like this...

    for(int i = 0;i<1000;i++){
        panel.add(new paintComponent());
        panel.setBackground(Color.black);
        frame.repaint();
        try {
            Thread.sleep(80);
        } catch (InterruptedException e1){e1.printStackTrace();}
    }
    

    Within the EDT. Apart from the fact you are adding multiple new components to your UI every 80 milliseconds, you are also blocking the thread that is responsible for updating your screen...

    Check out Concurrency in Swing for more details.

    Animation of this type should be handled by a javax.swing.Timer.

    Custom painting should be performed in the paintComponent method, as a general rule. You should also be calling super.paintXxx first as a matter of course. There's a lot of working going in the background that needs to be taken care of, especially if the component is transparent.

    Check out Performing Custom Painting and Painting in AWT and Swing for more details.

    Save your sanity and learn how to use layout managers, they will make your life easier in the long run.

    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 javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class MainFrame {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    MainFrame f = new MainFrame();
                }
            });
        }
        public JFrame frame = new JFrame();
        public JPanel panel = new JPanel(new BorderLayout());
        private WavePane waver;
    
        public MainFrame() {
            waver = new WavePane();
            JButton button1 = new JButton("Shweet Button");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(panel);
            panel.setBackground(Color.black);
            frame.add(button1, BorderLayout.SOUTH);
            button1.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    stuff();
                }
            });
            panel.add(waver);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public void stuff() {
    //        for (int i = 0; i < 1000; i++) {
    //            panel.add(new paintComponent());
    //            panel.setBackground(Color.black);
    //            frame.repaint();
    //            try {
    //                Thread.sleep(80);
    //            } catch (InterruptedException e1) {
    //                e1.printStackTrace();
    //            }
    //        }
            waver.walk(!waver.isWaving());
        }
    
        public class WavePane extends JComponent {
    
            private int options;
            private Timer timer;
    
            public WavePane() {
                setOpaque(false);
                timer = new Timer(80, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("tick");
                        options++;
                        repaint();
                    }
                });
                timer.setRepeats(true);
                timer.setCoalesce(true);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            public void walk(boolean walk) {
                if (walk) {
                    timer.start();
                } else {
                    timer.stop();
                }
            }
    
            public boolean isWaving() {
                return timer.isRunning();
            }
    
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2 = (Graphics2D) g;
                g2.setColor(Color.white);
    
                int height = getHeight();
    
                if (options % 2 == 0) {
                    g2.drawOval(50, 100, 50, 50);
                    g2.drawOval(60, 110, 8, 8);
                    g2.fillOval(60, 110, 8, 8);
                    g2.drawOval(80, 110, 8, 8);
                    g2.fillOval(80, 110, 8, 8);
                    g2.drawArc(65, 130, 100, 0, 140, 30);
                    g2.drawLine(75, 150, 75, 220);
                    g2.drawLine(75, 180, 100, 160);
                    g2.drawLine(75, 180, 65, 210);
                    g2.drawLine(75, 220, 50, 260);
                    g2.drawLine(75, 220, 100, 260);
                } else {
                    g2.drawOval(50, 100, 50, 50);
                    g2.drawOval(60, 110, 8, 8);
                    g2.fillOval(60, 110, 8, 8);
                    g2.drawOval(80, 110, 8, 8);
                    g2.fillOval(80, 110, 8, 8);
                    g2.drawArc(65, 130, 100, 0, 140, 30);
                    g2.drawLine(75, 150, 75, 220);
                    g2.drawLine(75, 180, 100, 180);
                    g2.drawLine(75, 180, 65, 210);
                    g2.drawLine(75, 220, 50, 260);
                    g2.drawLine(75, 220, 100, 260);
                }
            }
        }
    }
    

    You shouldn't be using JFrame.HEIGHT this actually has nothing to do with the frames height, but is part of the ImageObserver support... or any type of magic number for that matter