I have a JFrame with GridBagLayout, which contains a JLabel and a JScrollPane. The JScrollPane contains a ScrollablePanel, which in turn contains a certain amount of DebugComponents. The DebugComponents maintain an aspect ratio that is derived from the height of the JScrollPane. The layout of the JFrame with the GridBagLayout fits well at the beginning.
Now, however, I use a MouseMotionListener that changes the text of the JLabel indirectly via a thread. If the mouse is over the DebugComponent, there is a text displayed. Otherwise the area is empty. When I move to the DebugComponent, however, the layout changes. If I move the mouse back and forth between DebugComponent and ScrollablePanel, the space used by the JScrollPane gets bigger and bigger. It only stops when there is only space for the text for the JLabel left.
The code is an abstraction of my problem. Except for the class for the ScrollablePanel (get it here), the code is complete.
package de.franken.debug;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
public class DebugGUI {
private static JLabel label;
public static ScrollablePanel panel;
/** Main method + GUI configuration */
public static void main(String[] args) {
JFrame frame = new JFrame(); // JFrame
frame.setLayout(new GridBagLayout());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
GridBagConstraints c = new GridBagConstraints();
panel = new ScrollablePanel(new GridLayout(1, 0)); // Panel containing DebugItems
panel.setScrollableHeight(ScrollablePanel.ScrollableSizeHint.FIT);
for (int i = 0; i < 1; i++) {
panel.add(new DebugComponent(0.2));
}
c = new GridBagConstraints();
c.gridy = 1;
c.weighty = 0.6;
c.gridwidth = 1;
c.weightx = 1;
c.fill = GridBagConstraints.BOTH;
JScrollPane pane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); // ScrollPane containing Panel
new SlideThread(pane);
frame.add(pane, c);
label = new JLabel("PLACEHOLDER"); // Label displaying String
label.setHorizontalAlignment(SwingConstants.CENTER);
c = new GridBagConstraints();
c.gridy = 2;
c.weighty = 0.4;
c.fill = GridBagConstraints.BOTH;
c.gridwidth = 1;
c.weightx = 1;
frame.add(label, c);
frame.setSize(400, 400);
frame.setVisible(true);
}
/**
* Change text of JLabel beneath ScrollablePane. -> Called by SlideThread
*
* @param strg : the String to be displayed
*/
public static void setPreview(String strg) {
label.setText(strg);
}
static class DebugComponent extends JComponent {
private static final long serialVersionUID = -4770356342749347317L;
private double widthRatio;
/**
* JComponent as part of ScrollablePanel with constant dimension ratio
*
* @param widthRatio : the ratio to be multiplied with height to get width
*/
DebugComponent(double widthRatio) {
this.widthRatio = widthRatio;
}
@Override
public Dimension getPreferredSize() {
Dimension parent = super.getSize();
return new Dimension((int) (parent.height * widthRatio), parent.height);
}
@Override
protected void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(0, 0, (int) getPreferredSize().getWidth() - 1, (int) getPreferredSize().getHeight() - 1);
}
}
static class SlideThread extends Thread {
private String strg = " ";
SlideThread(JScrollPane pane) {
pane.addMouseMotionListener(adapter);
this.start();
}
@Override
public void run() {
while (true) {
try {
sleep(45);
} catch (InterruptedException e) {
e.printStackTrace();
}
DebugGUI.setPreview(strg);
}
}
// Show String during hovering
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
Component c = DebugGUI.panel.getComponentAt(e.getPoint());
if (c instanceof DebugComponent) {
strg = "Titel";
} else {
strg = " ";
}
}
};
}
}
GridBagLayout accounts only for visible components, so when your JLabel is blank, the layout treats it as a no-dimension component and acts as if it doesn't actually exist.
There are a couple of things you can try to avoid this issue:
a) Make the JLabel's text blank, rather than empty, e.g.
myLabel.setText(" ")rather thanmyLabel.setText("");b) Change the text's color to match the label's background color whenever there isn't supposed to be any text.
You may have some luck with putting the label into a BorderLayout within one cell of the GridBagLayout, in hopes that the "greedy" nature of the BorderLayout expands the component to take as much space as is available, but I haven't tried it myself, so I can't say for sure.