Search code examples
javaswingjpanellayout-managernull-layout-manager

jsplitpane and paintcomponent conflicting with each other


I'm having issues with my code regarding the fact that when I instantiate my City class as an object and add it to the right side of my JSplitPane (or even the left), the circle that is supposed to be drawn is not showing up. My cities class uses paintComponent and should draw a circle just by calling the constructor. I have also tried putting the repaint in its own drawIt() method but the result is still the same. The buttons and spinner show up on the left side of the divider, but the circle I am trying to draw does not show up at all.

Here is my City class.

import javax.swing.*;
import java.awt.*;

public class City extends JPanel{
    int xPos, yPos;
    
    City(int x, int y){
        xPos = x;
        yPos = y;
        repaint();
    }
    
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        
        g.setColor(Color.BLACK);
        g.fillOval(xPos, yPos, 10, 10);
    }
    
}

And here is my main. Here I try to instantiate my city and add it to the right side of the JSplitPane (under Add Components) and that is where I am having issues with, as the black circle will not be drawn on the JSplitPane.

import java.awt.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;

public class TSP{   
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            
            @Override
            public void run() {
                new TSP();
            }
        });
    }
    
    TSP(){
        JLabel instructions = new JLabel("Enter the number of cities: ");
        instructions.setBounds(30, 150, 300, 40);
        
        SpinnerNumberModel numMod = new SpinnerNumberModel(2, 2, 10, 1);
        JSpinner numOfCities = new JSpinner(numMod);
        numOfCities.setBounds(185, 150, 80, 40);
        
        JButton start = new JButton("Start Simulation");
        start.setBounds(50, 400, 200, 40);
        
        JFrame frame = new JFrame("Travelling Salesperson");
        
        JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        JPanel lp = new JPanel(null);
        JPanel rp = new JPanel(null);
        sp.setDividerLocation(300);
        sp.setLeftComponent(lp);
        sp.setRightComponent(rp);
        sp.setEnabled(false);
            
        frame.setDefaultCloseOperation(3);
        frame.setSize(1100,600);
        frame.setResizable(false);
        ////////////////Add Components//////////////////////////
        lp.add(instructions);
        lp.add(numOfCities);
        lp.add(start);
        
        City test = new City(301, 301);
        rp.add(test);
        
        frame.add(sp);
        ////////////////////////////////////////////////////////    
        
        frame.setVisible(true);
    }

}

I feel like the circle is being drawn under the JSplitPane as if I add my cities object (test) to my frame instead of the JSplitPane(frame.add(test) instead of rp.add(test) under the Add Components section) the black circle will appear in the desired spot but the JSplitPane along with the buttons and spinners will disappear so I feel as if they are conflicting. Is there any fix to this or is there another way altogether to make the circle appear on the right side while the other components are on the left side. I do not know why it is not drawing the circle on the JSplitPane, but any sort of help would be appreciated. Thanks! Sorry if anything is unclear or there is any ambiguity in my code, or if I need to post more information as I am quite new to posting here. Let me know if there is anything else I need to add or if there are any questions regarding what I am asking!

EDIT: It seems there is something blocking where I draw the circle, like another JPanel. Here is an image below. As you can see part of the circle looks as if it is being covered. The small box I drew is the only area that the dot is visible from (everywhere else the circle is covered up by white). Also, the coordinates for the circle in the image below is at (3, 0), i.e City test = new City(3, 0); I am not quite sure why this is happening though. the invisible JPanel?


Solution

  • Now that I've seen what you're trying to do, I can provide a more proper answer.

    You have a control panel on the left and a drawing panel on the right. Usually, you don't use a JSplitPane to separate the panels. To create your layout, you would add the control panel to the LINE_START of the JFrame BorderLayout and the drawing panel to the CENTER.

    The reason for this is that you don't want to constantly recalculate the size of the drawing panel.

    So let me show you one way to get a solid start. Here's the GUI I created.

    Traveling Salesperson GUI

    Here are the things I did.

    1. All Swing GUI applications must start with a call to the SwingUtilities invokeLater method. This method ensures that Swing components are created and executed on the Event Dispatch Thread.

    2. I separated the creation of the JFrame, the control panel, and the drawing panel. That way, I could focus on one part of the GUI at a time.

    3. The JFrame methods must be called in a certain order. This is the order that I use for most of my Swing applications.

    4. The JFrame is not sized. It is packed. The Swing layout managers will calculate the size of the components and the JPanels.

    5. I used a FlowLayout and a GridBagLayout to create the control panel. Yes, this looks more complicated than absolute positioning, but in the long run, layout managers allow the GUI to be more flexible.

    6. I used the setPreferredSize method in the drawing panel to set the preferred size of the drawing panel. Because I know the drawing panel size, I can put the first city in the center of the drawing panel.

    And here's the code. You don't have to code exactly like this, but this code should give you a good basis to start your project. Take a look at the model / view / controller pattern and see how to further separate your code into smaller pieces that allow you to focus on one part of your application at a time.

    I put all the classes in one file to make it easier to paste. You should separate these classes into separate files.

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JSpinner;
    import javax.swing.SpinnerNumberModel;
    import javax.swing.SwingUtilities;
    
    public class CitySimulation implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new CitySimulation());
        }
    
        private ControlPanel controlPanel;
    
        private DrawingPanel drawingPanel;
    
        private JFrame frame;
    
        @Override
        public void run() {
            frame = new JFrame("Traveling Salesperson");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            controlPanel = new ControlPanel();
            frame.add(controlPanel.getPanel(), BorderLayout.LINE_START);
    
            drawingPanel = new DrawingPanel();
            frame.add(drawingPanel, BorderLayout.CENTER);
    
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public class ControlPanel {
    
            private JPanel panel;
    
            public ControlPanel() {
                panel = new JPanel(new FlowLayout());
    
                JPanel mainPanel = new JPanel(new GridBagLayout());
    
                GridBagConstraints gbc = new GridBagConstraints();
    
                gbc.anchor = GridBagConstraints.LINE_START;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.insets = new Insets(10, 10, 10, 10);
    
                JLabel instructions = new JLabel("Enter the number " +
                        "of cities:");
                mainPanel.add(instructions, gbc);
    
                gbc.gridx++;
                gbc.insets = new Insets(10, 0, 10, 10);
    
                SpinnerNumberModel numMod =
                        new SpinnerNumberModel(2, 2, 10, 1);
                JSpinner numOfCities = new JSpinner(numMod);
                mainPanel.add(numOfCities, gbc);
    
                gbc.anchor = GridBagConstraints.CENTER;
                gbc.gridx = 0;
                gbc.gridy++;
                gbc.gridwidth = 2;
                gbc.insets = new Insets(10, 10, 10, 10);
    
                JButton start = new JButton("Start Simulation");
                mainPanel.add(start, gbc);
    
                panel.add(mainPanel);
            }
    
            public JPanel getPanel() {
                return panel;
            }
    
        }
    
        public class DrawingPanel extends JPanel {
    
            private static final long serialVersionUID = 1L;
    
            public DrawingPanel() {
                this.setBackground(Color.WHITE);
                this.setPreferredSize(new Dimension(400, 400));
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                g.setColor(Color.BLACK);
                g.fillOval(195, 195, 10, 10);
            }
            
        }
    
    }