Zyxl
Zyxl

Reputation: 124

Java - Redrawing a component from within a separate component

I'm very new to GUI programming in Java and have come up against a problem where changes in one component cause another component (not a parent or child or grandparent or grandchild etc.) to have to be redrawn. More specifically, I have a window as a winGUI (a subclass of JFrame) with a BorderLayout contentPane, the CENTER of which contains a component with the following method which runs when the component is clicked:

winGUI window = (winGUI)(getParent().getParent().getParent().getParent());
window.updatePanel(panelContent);

(I suspect there's a much better way of retrieving the window, so that would be good to know.)

The class definition of winGUI is as follows

import java.awt.BorderLayout;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class winGUI extends JFrame {
    private UIController userInterface;
    private JPanel content;

    public static void main(String[] args) {
        JFrame window = new winGUI();
        window.setResizable(false);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
    }

    public winGUI() {
        super("Window");
        setSize(Toolkit.getDefaultToolkit().getScreenSize());
        setResizable(false);
        content = new JPanel();
        content.setLayout(new BorderLayout());
        userInterface = new UIController();
        content.add(userInterface.getCenter(), BorderLayout.CENTER);
        content.add(userInterface.getPanel(), BorderLayout.WEST);
        setContentPane(content);
        setLocation(0, 0);
    }

    public void updatePanel(PanelContent panelContent) {
        userInterface.buildPanel(panelContent);
        content.add(userInterface.getPanel(), BorderLayout.WEST);
    }

}

And here are the important bits of the UIController class:

private JPanel sidePanel;

public UIController() {
    buildPanel(null);
}

public void buildPanel(PanelContent panelContent) {
    sidePanel = new JPanel();
    sidePanel.setBackground(new Color(220, 220, 220));
    sidePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
    sidePanel.setPreferredSize(new Dimension(250, 500));
    if (panelContent != null) {
        sidePanel.add(new JLabel(panelContent.getLabel()));
    }
}

public JPanel getPanel() {
    return sidePanel;
}

After calling userInterface.buildPanel(panelContent) from within the updatePanel method, I was expecting the panel to change because the getPanel() method passes by reference, and so the component added to content when the window was created should be updated as well as sidePanel because they reference the same object. However, this did not happen, so I tried adding

content.add(userInterface.getPanel(), BorderLayout.WEST);
userInterface.getPanel().repaint();
this.repaint();

to the end of the updatePanel method, but to no avail.

Any help would be very much appreciated.

Upvotes: 1

Views: 697

Answers (1)

NESPowerGlove
NESPowerGlove

Reputation: 5496

You can use the observer pattern (creating your own Swing like listener) to pass the event to winGui that it should update it's panel.

interface SomeUpdateListener {
     public void onUpdate(PanelContent panelContent);
}

Then have winGui be such a listener:

public class winGUI extends JFrame implements SomeUpdateListener
    ...
    @Override
    public void onUpdate(PanelContent panelContent) {
        userInterface.buildPanel(panelContent);
        content.add(userInterface.getPanel(), BorderLayout.WEST);
        // Fix for your last issue I believe
        this.revalidate();  // Pretty sure this is what's needed in your case
        this.repaint(); // redraws them
    }

I have no idea what the custom component actually use to call updatePanel is or how it works, but let's assume it's called SomeCustomComponent with roughly the following code now:

public class SomeCustomComponent extends JComponent {
    private SomeUpdateListener listener;
    public SomeCustomComponent(SomeUpdateListener someListener) {
         this.listener = someListener;
    }
    ...
    private void timeToMakeThatPanelContentUpdate() {
         PanelContent newContent = createNewPanelContents();
         listener.onUpdate(newContent);
    }
    ...

So now with this new "listener" type, you don't have a direct circular like reference between your parent (or eventually parent I suppose here...), and the child component in your application code.

Note I added in a couple lines to fix the last issue of the panel not updating with the latest contents in the onUpdate() method.

this.revalidate(); // Pretty sure this is what's needed in your case
this.repaint();

Also if the panel you are creating is not actually any different through some GUI component combinations, but instead only different from the information it displays, then perhaps you should just pass data that represents what the preexisting GUI components should now display.

Upvotes: 1

Related Questions