Question

There is a panel in a JFrame called a WatchPanel extends JPanel which uses a GroupLayout and contains a WatchListPanel extends JPanel. The WatchListPanel uses a null LayoutManager and contains several instances of WatchItemPanel extends JPanel. The WatchItemPanel lays out its contents with a GridBagLayout.

When I load up the frame and instantiate the WatchPanel, I pre-load the WatchListPanel with three WatchItemPanel instances with data. These work fine. My implementation allows these to be reordered by dragging and dropping (hence the null layout), and that works as well. On the WatchPanel is a button to add a new watch, which pops up a new panel in its own JFrame where a watch can be added. When the "Save" button is pressed on this new panel, it adds the new watch to the WatchPanel, which trickles down into the WatchListPanel and closes the JFrame for adding a new watch.

The problem is that when this new WatchItemPanel is added, it doesn't get painted unless I resize the JFrame containing the WatchPanel. I can interact with it through drag-n-drop. I can even drag that specific panel - but it's blank. The moment I resize the WatchPanel, it appears and is painted correctly.

Now, I have overridden the setBounds method on WatchListPanel to call setSize and repaint on each of the WatchItemPanel instances in the list, but I am calling setBounds and repaint on the new WatchItemPanel already as I add it. I have tried adding repaint and invalidate calls in every conceivable place thinking maybe I missed something, but no luck. I absolutely have added the WatchItemPanel to the WatchListPanel and called setBounds, setVisible(true), and repaint on the new panel.

Here are the addWatch and updatePositions methods on the WatchListPanel:

public void addWatch(Watch watch) {
    WatchItemPanel watchPanel = new WatchItemPanel(watch);
    synchronized (this.watches) {
        this.watches.add(watch);
    }
    this.watchPanels.put(watch, watchPanel);
    this.add(watchPanel);
    watchPanel.setOpaque(false);
    watchPanel.setForeground(this.getForeground());
    watchPanel.setBackground(this.getBackground());
    watchPanel.setVisible(true);
    watchPanel.setSize(this.getWidth(), WatchItemPanel.PANEL_HEIGHT);
    for (MouseListener l: this.mouseListeners)
        watchPanel.addMouseListener(l);
    for (MouseMotionListener l: this.mouseMotionListeners)
        watchPanel.addMouseMotionListener(l);
    this.updatePositions();
}

private void updatePositions() {
    int count = 0;
    synchronized (this.watches) {
        for (Watch watch: this.watches) {
            if ((this.selectedWatchPanel != null) && (count == this.selectedWatchPanelPosition))
                count++;
            WatchItemPanel panel = this.watchPanels.get(watch);
            if (panel == this.selectedWatchPanel)
                continue;
            panel.setBounds(0, count * WatchItemPanel.PANEL_HEIGHT, this.getWidth(), WatchItemPanel.PANEL_HEIGHT);
            count++;
        }
    }
    if (this.selectedWatchPanel != null)
        this.selectedWatchPanel.setBounds(0, (this.selectedWatchPanelPosition * WatchItemPanel.PANEL_HEIGHT) + this.selectedWatchPanelDragOffset, this.getWidth(), WatchItemPanel.PANEL_HEIGHT);
    this.repaint();
}

And here are the setBounds methods on WatchListPanel:

@Override
public void setBounds(Rectangle r) {
    super.setBounds(r);
    for (WatchItemPanel panel: this.watchPanels.values()) {
        panel.setSize(r.width, WatchItemPanel.PANEL_HEIGHT);
        panel.repaint();
    }
}

@Override
public void setBounds(int x, int y, int width, int height) {
    super.setBounds(x, y, width, height);
    for (WatchItemPanel panel: this.watchPanels.values()) {
        panel.setSize(width, WatchItemPanel.PANEL_HEIGHT);
        panel.repaint();
    }
}

Another piece of information here - all four panels are being painted. I overrode paintComponent in the WatchItemPanel and added a System.out.println and got this result:

WatchItemPanel[,0,66,366x22,invalid,layout=java.awt.GridBagLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777219,maximumSize=,minimumSize=,preferredSize=]
WatchItemPanel[,0,44,366x22,layout=java.awt.GridBagLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777219,maximumSize=,minimumSize=,preferredSize=]
WatchItemPanel[,0,22,366x22,layout=java.awt.GridBagLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777219,maximumSize=,minimumSize=,preferredSize=]
WatchItemPanel[,0,0,366x22,layout=java.awt.GridBagLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777219,maximumSize=,minimumSize=,preferredSize=]

What's that extra "invalid" column there in the panel? When I resize the WatchPanel the "invalid" goes away. It looks like an extra column, though. There's no corresponding non-invalid value on the other lines. (this is the default JPanel.toString() output with the package name removed.

Was it helpful?

Solution

After adding the component, call this.revalidate(). This causes the container to be marked to re-layout its components. Once this line of code is added, it works perfectly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top