Search code examples
javagridbaglayout

How to set Layout with another class in Java?


I'm writing a programm with a complicated TabbedPane, with lots of elements. Since I hit the Byte-limit in one of my classes, I decided to split the class into Initialisation/Button Listeners and the actual GridBagLayout. But now I'm having trouble getting it to work. My Main Class looks like this:

public class Main{
JFrame mainFrame = new JFrame("");
JTabbedPane tabpane = new JTabbedPane();
JPanel panelTab1 = new Tab1();
JScrollPane scrollTab2 = new JScrollPane(new Tab2());
JScrollPane scrollTab3 = new JScrollPane(new Tab3());
JPanel panelTab4 = new Tab4();
JMenuBar bar = new MenuBar();


public Main(){
    tabpane.add("Tab1", panelTab1);
    tabpane.add("Tab2", scrollTab2);
    tabpane.add("Tab3", scrollTab3);
    tabpane.add("Tab4", panelTab4);
    mainFrame.getContentPane().add(tabpane);
    mainFrame.setSize(1920,1080);   
    mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
    mainFrame.setVisible(true);     
    mainFrame.validate();
    mainFrame.setJMenuBar(bar);

}



public static void main(String[] args)
{

    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            new Main();
        }
    });


}}

Tab3 is the culprit so here are the two classes I split.

public class Tab3 extends JPanel {
JPanel Annex = new JPanel();
//A Bunch of Labels and Buttons
.
.
.
public Tab3(){
//ActionListeners for the Buttons
this.setLayout(new BorderLayout());
this.add(Annex,BorderLayout.WEST);
this.add(Bsp,BorderLayout.EAST);
}}

All the GridBagLayout is in the following class:

public class Tab3Layout extends Tab3{
    public Tab3Layout(){
        Annex.setLayout(new GridBagLayout());
        GridBagConstraints co1 = new GridBagConstraints();
        co1.gridx = 0;
        co1.gridy = 0;
        co1.anchor = GridBagConstraints.FIRST_LINE_START;
        co1.weighty = 1.0;
        Annex.add(Annex1, co1);
        //and so on...
        }}

Now my question is, how do I get this to work? Right now if I compile, Tab3 is just empty. If everything is in one class it works exactly how I want, but the code is just much too long. Seems to me like I'm missing a line in the Tab3 class, but even after hours of tinkering and searching I have no idea how to solve this. Everything I tried just produced more errors.


Solution

  • The problem is in iheritance. Tab3Layout has its own Annex JPanel (inherited from Tab3), which it is modifying and adding components to, while Tab3 class is inserting its Annex, which is initialized via new JPanel() and you do nothing else with it. Tab3Layout class never even touches Annex panel you intialize and add in Tab3 .

    public class Tab3 extends JPanel {
    JPanel Annex = new JPanel(); // <----- this, an empty JPanel
    //A Bunch of Labels and Buttons
    
    public Tab3(){
    //ActionListeners for the Buttons
    this.setLayout(new BorderLayout());
    this.add(Annex,BorderLayout.WEST); // <----- is inserted here
    this.add(Bsp,BorderLayout.EAST);
    }}
    

    You will get an error if you delete new JPanel(), which will illustrate that both classes are doing work on different objects, which just happen to be named the same. You need to pass all components/panels you want to appear in Tab3 to Tab3Layout to modify, then you can retrieve them later with getters. Another way would be to create AnnexPanel class, which itself would be extending JPanel class. It would set its own layout, components etc. and be able to be added directly. Following Single Responsibility Principle would be best, if your classes grow in size. If it's 200+ lines of code, you should consider refactoring it. If you will allow your class to grow like 3000+ lines of code, you will have a bad time. Try to forsee where you can create new class/method in early stages of development. See KISS principle .

    Well..I would split those in different way.

    My Tab3Layout class. Annex panel is injected via constructor, you can set a setter for that, but remember to not call createAnnex() before injection. Anyway, compiler will make you remember.

    public final class Tab3Layout {
    
        private JPanel Annex;
        JPanel Annex1; //just to avoid compiler error
        private GridBagLayout gridBagLayout;
        private GridBagConstraints co1;
    
        public Tab3Layout(JPanel Annex) {
            this.Annex = Annex;
            this.gridBagLayout = new GridBagLayout();
            this.co1 = new GridBagConstraints();
        }
    
        public void createAnnex() {
            this.Annex.setLayout(gridBagLayout);
    
            this.co1.gridx = 0;
            this.co1.gridy = 0;
            this.co1.anchor = GridBagConstraints.FIRST_LINE_START;
            this.co1.weighty = 1.0;
    
            this.Annex.add(Annex1, co1);
        }
    
        public JPanel getAnnex() {
            return this.Annex;
        }
    }
    

    And then the Tab3 class:

    public class Tab3 extends JPanel {
    
        JPanel Annex;
        Tab3Layout tab3Layout;
    
        public Tab3() {
            super();
            this.Annex = new JPanel();
    
            this.tab3Layout = new Tab3Layout(Annex);
            this.tab3Layout.createAnnex();
    
            this.setLayout(new BorderLayout());
            this.add(tab3Layout.getAnnex(), BorderLayout.WEST);
            this.add(new JPanel(), BorderLayout.EAST);
        }
    }
    

    Look at my GridBagManager repo, it can give you an idea how to set layout with another class.

    You can also extend Tab3Layout with JPanel. And remember to set anchor and fill with GridBagConstraints.

    And Java naming convention.

    JPanel Annex = new JPanel();
    GridBagConstraints co1 = new GridBagConstraints();
    

    to

    JPanel annexPanel = new JPanel();
    GridBagConstraints gridBagConstraints = new GridBagConstraints();
    

    Personally I use gbc for GridBagConstraints object, as you will see in my repo I mentioned above.

    Edit: I think you should split your implementation into couple of smaller classes. That would be professional aproach. Refactor your code. For starters you can make every panel its own class extending JPanel (like i suggested with AnnexPanel class).

    Edit2: createAnnex() can return JPanel type, reducing your boilerplate code, your call then would be as follows:

    this.add(Tab3Layout.createAnnex(), co1);
    

    instead of

    Tab3Layout.createAnnex();
    this.add(Tab3Layout.getAnnex(), co1);
    

    Maybe even take createAnnex() to constructor (assuming class extends JPanel) if it's a singleton.