Search code examples
javaswingcanvas

How to add a Canvas and other Components to a Swing GUI


I'm trying to create and application with Swing that includes a drawable object, which can be drawn on at runtime by calling commands with Action Listeners.

The Problem is that each time I'm adding either a Canvas or a JPanel, it won't be displayed unless I make it take up the entire Frame which is not my goal.

The current code looks like this:

public class LRGS {

    public static void GUI() {
        Graph mainGraph = new Graph();
        JFrame frame = new JFrame("LRG – Type S");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        JPanel mainPanel = new JPanel();
        JPanel nPanel = new JPanel();
        JButton j = new JButton("fesdf");
        nPanel.add(j);
        mainPanel.add(nPanel);
        mainPanel.add(mainGraph);
        frame.add(mainPanel);

        // Display the window.
        frame.setVisible(true);

        mainGraph.l();

    }

    public static void main(String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                GUI();
            }
        });
    }
}

class Graph extends JPanel {
    JPanel graphPanel;
    Canvas graph;
    Graphics g;

    public Graph() {
        // Create Panel for Components
        graphPanel = new JPanel();
        graph = new Canvas();
        graph.setSize(122, 122);
        graphPanel.add(graph);
        g.drawString("H", 100, 100);
    }

    public void l() {
        repaint();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawString("Hello World!", 100, 100);
    }

}

I apologize for the mess that it is, but I'm circling around this problem for hours now and don't really know where I'm standing.

What I've tried: 1. I tried to add a JPanel which serves as a drawable object into the Main Panel which did not work.

2. I tried to use GridBagLayout to tell Java that I want the Canvas/drawable Panel of my class next to a Component/other Panel.

3. I tried adding it without an extra class (although I prefer to have it in a separate class).

What I expected: I thought one could simply add a Canvas to a Swing GUI using some Panels. As mentioned before my goal is to have other components in it as well, so I can pass input through them in order to display some output on the Canvas/drawable Panel.

I'm also a bit confused about how to use Graphics, as some examples I've seen in tutorials and here on StackOverflow implement it differently and some others implement it how I did.

What actually resulted: Result

What I've got now is a Frame with just one of the Panels (I believe, as the Canvas/drawable JPanel has not been displayed with any of my attempts) and it's button. Not exactly what I wanted.

I'm aware that I could potentially open a secondary frame which would display the Canvas/drawable Panel, but I'd rather have it all in one place, which is why I am here.


Solution

  • First of all...

    JPanel graphPanel;
    Canvas graph;
    Graphics g;
    
    public Graph() {
        // Create Panel for Components
        graphPanel = new JPanel();
        graph = new Canvas();
        graph.setSize(122, 122);
        graphPanel.add(graph);
        g.drawString("H", 100, 100);
    }
    

    No and no. This is not how custom painting works in Swing. Swing has a well defined and documented paint workflow and you should learn it and work with it.

    See Painting in AWT and Swing and Performing Custom Painting for more details.

    Second, a component has a default preferred size of 0x0, so when adding it to your container, the layout manager will, in most case, simply ignore it.

    In this case, you should consider providing a sizing hint, for example...

    class Graph extends JPanel {
    
        public Graph() {
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawString("Hello World!", 100, 100);
        }
    }
    

    And lastly, you should pack the frame, frame.pack(); before it's made visible, this will allow the window to adapt itself around the sizing requirements of its content.

    Runnable example...

    enter image description here

    import java.awt.Dimension;
    import java.awt.Graphics;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Main {
    
        public Main() {
            Graph mainGraph = new Graph();
            JFrame frame = new JFrame("LRG – Type S");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
    
            JPanel mainPanel = new JPanel();
            JPanel nPanel = new JPanel();
            JButton j = new JButton("fesdf");
            nPanel.add(j);
            mainPanel.add(nPanel);
            mainPanel.add(mainGraph);
            frame.add(mainPanel);
            frame.pack();
            // Display the window.
            frame.setVisible(true);
    
        }
    
        public static void main(String[] args) {
            // Schedule a job for the event-dispatching thread:
            // creating and showing this application's GUI.
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new Main();
                }
            });
        }
    
        class Graph extends JPanel {
    
            public Graph() {
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.drawString("Hello World!", 100, 100);
            }
        }
    }
    

    Please, also beware, Canvas is a heavy weight component, it doesn't have a concept of z-order and may not play well with other Swing based components (it's suppose to have been fixed, but I've never had any luck making it work).

    Canvas tends to be used when you want to implement your own painting loop and want a low level access to the graphics hardware, usually driven by a BufferStrategy. In your case, it's use will probably cause you more issues then it solves.