Search code examples
javaswingjframejpanelgraphics2d

Java graphics window doesn't draw when opened from another window through an event


I have three classes, the first one is the main window smthn like

public class Starter extends JFrame{
JButton b=new JButton("Game");
Starter(){

    setSize(200,200);
    add(b);
    b.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent e){
            TryGraph gg=new TryGraph();
        }
    });
    setVisible(true);

}
public static void main(String [] args){
    Starter g= new Starter();

}

}

Then the second class is a window that has a panel where the graphics is going to be drawn on

public class TryGraph {
static int w=640,h=480;
TryGraph(){
    JFrame mF=new JFrame();
    GPan pan=new GPan();

    mF.setLocationRelativeTo(null);
    mF.setResizable(false);
    mF.setSize(w,h);

    mF.add(pan);

    mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mF.setVisible(true);

    pan.playGame();
}

public static void main(String []args){
    TryGraph t=new TryGraph();
}

}

Then lastly the panel class which does the drawing

public class GPan extends JPanel{

private boolean running;
private BufferedImage image;
private Graphics2D g;
int x,y;

public GPan(){
    x=TryGraph.w;
    y=TryGraph.h;
    init();
}

public void init(){
    running=true;
    image=new BufferedImage(x,y,BufferedImage.TYPE_INT_RGB);
    g=(Graphics2D)image.getGraphics();
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}

public void playGame(){
    while(running){
         g.setColor(new Color(102,102,102));
        g.fillRect(0,0,x,y);
        g.setColor(new Color(255,0,0));
        g.fillOval(0, 0, 100,100);
        repaint();
    }
}

public void paintComponent(Graphics g){
    Graphics2D g2 = (Graphics2D)g;
    g2.drawImage(image,0,0,x,y,null);

    g2.dispose();
}

}

The problem is if I'm using an event from the Starter class window to the TryGraph class window the GPan panel won't draw the graphics when it loops through play game() method. But when the TryGraph class is executed directly it works fine


Solution

  • A couple of problems I noticed.

    1. When you call main in TryGraph, you create a new instance. But you already have an instance of TryGraph when the button was pressed.

    2. Don't extend JFrame. It's bad practice. Just create an instance of it.

    3. Only use one static main entry point.

    4. The big problem is that you put repaint() in a tight loop. Don't do that. Just call repaint(). Remember that repaint is run on the Event Dispatch Thread. So if that thread is tied up, nothing else will work.

    5. Don't dispose of your graphics context. You can do that if you create a new one but otherwise don't get rid of it.

    6. Finally, put super.paintComponent() as the first statement in your JPanel paintComponent method.

    I suggest you read about painting and the EDT in the Java Tutorials.

    Here is the modified code, all in one file.

    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Starter {
       JButton b     = new JButton("Game");
       JFrame  frame = new JFrame();
    
       Starter() {
    
          frame.setSize(200, 200);
          frame.add(b);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          b.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                TryGraph gg = new TryGraph();
             }
          });
          frame.setVisible(true);
    
       }
       public static void main(String[] args) {
          Starter g = new Starter();
    
       }
    }
    
    class TryGraph {
       static int w = 640, h = 480;
    
       TryGraph() {
          JFrame mF = new JFrame();
          GPan pan = new GPan();
    
          mF.setLocationRelativeTo(null);
          mF.setResizable(false);
          mF.setSize(w, h);
    
          mF.add(pan);
    
          mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          mF.setVisible(true);
    
          pan.playGame();
       }
    
    }
    
    class GPan extends JPanel {
    
       private boolean       running;
       private BufferedImage image;
       private Graphics2D    g;
       int                   x, y;
    
       public GPan() {
          x = TryGraph.w;
          y = TryGraph.h;
          init();
       }
    
       public void init() {
          running = true;
          image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
          g = (Graphics2D) image.getGraphics();
          g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
       }
    
       public void playGame() {
          repaint();
    
       }
    
       public void paintComponent(Graphics g) {
          super.paintComponent(g);
          Graphics2D g2 = (Graphics2D) g.create();
    
          g2.setColor(new Color(102, 102, 102));
          g2.fillRect(0, 0, x, y);
          g2.setColor(new Color(255, 0, 0));
          g2.fillOval(0, 0, 100, 100);
          // g2.drawImage(image,0,0,x,y,null);
    
          g2.dispose();
       }
    }
    

    One additional suggestion. In your original code I believe you implemented a listener by implementing the interface in a private class. Good idea except you should extend the adapter class for the given interface. For example, for mouseListener, extend MouseAdapter. It provides dummy methods so you don't have to create them yourself. It helps make your code more readable. Of course, single method interfaces don't have ( or need) adapters.