3

While working on a program, I made some of my fields static i.e. a JTextField for a E-Number. But now this field behaves not as expected, on some of my pages it appears, on some others it disappears. Since I am not very experienced working with a lot of statics, there might be a concept I am not understanding.

I have created a very simplified but working example of my program (MCVE) if you want to test it.

It shows my overview page at first - the E-Number JTextField is missing.

If you click on the search button, it shows the tracking page - with the E-Number JTextField present.

Both pages contain the same workNumberPanel and I cant find a difference, that would explain the behaviour.

So why is the E-Number JTextField present on the overview page and missing on the tracking page? Any help / explanation is appreciated!

Static JTextField disappears

MainProgram.java

import java.awt.CardLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;

import net.miginfocom.swing.MigLayout;

public class MainProgram extends JFrame {
    private static final long serialVersionUID = 1L;

    public static JPanel centerPanel = new JPanel();

    public static CardLayout contentCardsLayout = new CardLayout();

    OverviewPage overviewPage = new OverviewPage();
    TrackingPage trackingPage = new TrackingPage();

    public void initialize() {
        createCenterPanel();
    }

    private void createCenterPanel() {
        centerPanel.setLayout(contentCardsLayout);

        overviewPage.setName("overviewPage");
        trackingPage.setName("trackingPage");

        centerPanel.add(overviewPage, "overviewPage");
        centerPanel.add(trackingPage, "trackingPage");

        add(centerPanel, "growx, wrap");
    }

    public MainProgram() {
        setBounds(300, 50, 1200, 900);
        setLayout(new MigLayout());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
                    MainProgram window = new MainProgram();
                        window.setVisible(true);
                        window.initialize();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

OverviewPage.java

import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

public class OverviewPage extends JPanel {
    WorkNumberPanel workNumberPanel = new WorkNumberPanel();

    private static final long serialVersionUID = 1L;

    public OverviewPage() {
        setLayout(new MigLayout());
        add(workNumberPanel, "wrap, growx");
    }
}

TrackingPage.java

import javax.swing.JPanel;

import net.miginfocom.swing.MigLayout;

public class TrackingPage extends JPanel {
    private static final long serialVersionUID = 1L;

    WorkNumberPanel equipmentNumberPanel = new WorkNumberPanel();

    public TrackingPage(){
        setLayout(new MigLayout("", "grow, fill"));
        add(equipmentNumberPanel, "wrap, growx");
    }
}

WorkNumberPanel.java

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import net.miginfocom.swing.MigLayout;

public class WorkNumberPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private static final Integer TEXTFIELD_LENGTH = 20;

    JPanel mainWorkNumberPanel = new JPanel();

    JLabel lblWorkNumber = new JLabel("E-Nr: ");
    JLabel lblN_Number = new JLabel("N-Nr.: ");
    JLabel lblSNumber = new JLabel("W-Nr.: ");

    public static JTextField txtWorkNumber = new JTextField(TEXTFIELD_LENGTH);
    JTextField txtNNumber = new JTextField(TEXTFIELD_LENGTH);
    JTextField txtSNumber = new JTextField(TEXTFIELD_LENGTH);

    JButton btnSearchEntry = new JButton("Search");

    public WorkNumberPanel() {
        createEquipmentNumberPanel();
        btnSearchEntry.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                MainProgram.contentCardsLayout.show(MainProgram.centerPanel, "trackingPage");
            }
        });
    }

    private void createEquipmentNumberPanel() {
        setLayout(new MigLayout());
        mainWorkNumberPanel.setLayout(new MigLayout("", "[][grow, fill][][grow, fill][][grow, fill][]"));
        mainWorkNumberPanel.add(lblWorkNumber);
        mainWorkNumberPanel.add(txtWorkNumber);
        mainWorkNumberPanel.add(lblN_Number);
        mainWorkNumberPanel.add(txtNNumber);
        mainWorkNumberPanel.add(lblSNumber);
        mainWorkNumberPanel.add(txtSNumber);
        mainWorkNumberPanel.add(btnSearchEntry);

        add(mainWorkNumberPanel, "push, span, growx");
    }
}
Community
  • 1
  • 1
hamena314
  • 2,969
  • 5
  • 30
  • 57

3 Answers3

3

Probably because when you create your "pages" with this code

OverviewPage overviewPage = new OverviewPage();
TrackingPage trackingPage = new TrackingPage();

the TrackingPage will be the last one to execute the following line

mainWorkNumberPanel.add(txtWorkNumber);

in private void createEquipmentNumberPanel(), and hence that Panel will "own" the JTextField. It only makes sense that a UI component can only be at one place at any given time, otherwise things would get very strange :)

Your statement

Both pages contain the same workNumberPanel and I cant find a difference, that would explain the behaviour.

is simply not true. You are creating a new instance of WorkNumberPanel in both OverViewPage and TrackingPage when you execute the following line

WorkNumberPanel equipmentNumberPanel = new WorkNumberPanel();

So my recommendation is that you find another way of implementing what you want without using a static JTextField (or any other UI component for that matter).

Magnus
  • 17,157
  • 19
  • 104
  • 189
  • Both answers make it very clear, what my error was. Now I have to find a way to use the `JTextField` without static. I am creating a `OrderObject`, which I send from the overview page to the tracking page. There I want to show the order number (E-Number) in the `JTextField`. But I have not yet figured out, how the overview page can tell the tracking page to set the text to the E-Number, hence my usage of static, since this seems to be simple, yet wrong? – hamena314 Mar 31 '16 at 10:50
  • Yes, using `static` in this way is not a solution. You have to get the result of the first dialog and insert it into the second one. This can be done in a number of ways, I prefer [this method](http://stackoverflow.com/a/4089370/930640). – Magnus Mar 31 '16 at 11:14
  • Those are not dialogues. I have a page, on which is a navigation panel (left side), number panel (header) and the rest is content (center). I use a cardlayout to switch from one page to another and have the need, to keep the same numbers in the number panel in the header on each page, since they all belong to the same order. – hamena314 Mar 31 '16 at 11:41
  • Sorry, my bad. Either way, you should never use a static UI component. Simply store the value from one panel and display it in the other one when it is displayed. – Magnus Mar 31 '16 at 12:02
2

Here you instantiate a OverviewPage, then a TrackingPage .

OverviewPage overviewPage = new OverviewPage();
TrackingPage trackingPage = new TrackingPage();

Both of these classes instantiate a WorkNumberPanel .

WorkNumberPanel add the static JTextField (txtWorkNumber) to their display panel (mainWorkNumberPanel).

A single Component can't be added to several Container objects. This is what happens to your textfield, since it is static and not an instance variable.

The last addition will win, so the textfield will appear in TrackingPage only, and not in OverviewPage anymore .

Just don't make it static.

Arnaud
  • 17,229
  • 3
  • 31
  • 44
  • 1
    My misconception seems to be to abuse `static` as an easy way to transfer information / data from one class to another. I need to figure out, when to create instances / objects and how to give them the information they need. – hamena314 Mar 31 '16 at 10:53
1

First off you need to understand what a static field is. A static field is not related to a particular instance of an object. The field is related to the class itself.
There's quite a good explanation here and here.


Now with regards to your case. A JComponent can only be added to one panel at a time. Adding it to another will remove it from the first.

In your code you are creating multiple instances of 'WorkingNumberPanel'. When you do this you add the text fields to the panel, including the static text field txtWorkNumber. Since the field txtWorkNumber is static you are adding the same object to multiple components, which as I mentioned above will remove it from anywhere it was previously added.

One possible way of solving this would be to store the value from txtWorkNumber in a static variable and create a new instance (non-static) text field to add to the panel.

Community
  • 1
  • 1
Tiz
  • 680
  • 5
  • 17