Why does my new component not display in a null layout manager?
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.Gri开发者_运维百科dBagLayout,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.
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.
精彩评论