1

Inspired by this question JLayeredPane with a LayoutManager I'm trying to get the JLayeredPane to work with the GridBagLayout.

Here's the custom LayeredPane-class:

class StackConstraints {
    public final int layer;
    public final Object layoutConstraints;

    public StackConstraints(int layer, Object layoutConstraints) {
        this.layer = layer;
        this.layoutConstraints = layoutConstraints;
    }
}

class LXLayeredPane extends JLayeredPane {

    private static final long serialVersionUID = 1946283565823567689L;

    @Override
    protected void addImpl(Component comp, Object constraints, int index) {
        int layer = 0;
        int pos = 0;
        Object constr = null;
        if (constraints instanceof StackConstraints) {
            layer = ((StackConstraints) constraints).layer;
            constr = ((StackConstraints) constraints).layoutConstraints;
        } else {
            layer = getLayer(comp);
            constr = constraints;
        }

        pos = insertIndexForLayer(layer, index);
        super.addImpl(comp, constr, pos);
        setLayer(comp, layer, pos);
        comp.validate();
        comp.repaint();
    }
}

And here's a simple demo (similar to the standard-JLayeredPane demo but adapted for the usage of GridBagConstraints and stripped of the unnecessary stuff).

public class LayeredPaneDemo extends JPanel implements ActionListener {
    private final Color[] layerColors = { Color.yellow, Color.magenta, Color.cyan, Color.red, Color.green, Color.blue };

    private final JLayeredPane layeredPane;
    private final List<JLabel> labels;

    private JButton update;

    public LayeredPaneDemo() {
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        labels = new ArrayList<>();

        layeredPane = new LXLayeredPane();
        layeredPane.setPreferredSize(new Dimension(400, 410));
        layeredPane.setBorder(BorderFactory.createTitledBorder("Click to change colors"));

        // Add several labels to the layered pane.
        layeredPane.setLayout(new GridBagLayout());
        for (int i = 0; i < layerColors.length; i++) {
            JLabel label = createColoredLabel("Test", layerColors[i]);
            labels.add(label);
            layeredPane.add(label, new StackConstraints(i, gbc(i)));
        }

        // Add control pane and layered pane to this JPanel.
        add(Box.createRigidArea(new Dimension(0, 10)));
        add(createControlPanel());
        add(Box.createRigidArea(new Dimension(0, 10)));
        add(layeredPane);
    }

    private GridBagConstraints gbc(int i) {
        return new GridBagConstraints(i, i, 2, 2, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
                new Insets(0, 0, 0, 0), 0, 0);
    }

    // Create and set up a colored label.
    private JLabel createColoredLabel(String text, Color color) {
        JLabel label = new JLabel(text);
        label.setVerticalAlignment(JLabel.TOP);
        label.setHorizontalAlignment(JLabel.CENTER);
        label.setOpaque(true);
        label.setBackground(color);
        label.setForeground(Color.black);
        label.setBorder(BorderFactory.createLineBorder(Color.black));
        label.setPreferredSize(new Dimension(240, 240));
        return label;
    }

    // Create the control pane for the top of the frame.
    private JPanel createControlPanel() {
        update = new JButton("Update");
        update.addActionListener(this);
        update.setActionCommand("UPDATE");

        JPanel controls = new JPanel();
        controls.add(update);
        controls.setBorder(BorderFactory.createTitledBorder("Choose Duke's Layer and Position"));
        return controls;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Color prev = labels.get(labels.size() - 1).getBackground();

        for (int i = labels.size() - 1; i > 0; --i) {
            labels.get(i).setBackground(labels.get(i - 1).getBackground());
            labels.get(i).validate();
            labels.get(i).repaint();
        }
        labels.get(0).setBackground(prev);
        labels.get(0).validate();
        labels.get(0).repaint();
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("LayeredPaneDemo2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JComponent newContentPane = new LayeredPaneDemo();
        newContentPane.setOpaque(true);
        frame.setContentPane(newContentPane);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

}

My problem is that I'm adding 6 (six!) labels but only 5 (five!) are displayed. The 0-th one just vanishes somewhere. What's the reason for that?

EDIT: The initial motivation behind the pursuit is to place one (partially transparent) component on top of another like in this screenshot enter image description here The label displaying 17:00:09 has transparent background and is placed on top of the chart-component. GridBagLayout is needed to place it exactly at the top middle of the chart.

Community
  • 1
  • 1
Mischa
  • 215
  • 1
  • 9
  • `I'm trying to get the JLayeredPane to work with the GridBagLayout.` - why? This is two different concepts. A layered pane is for displaying components in a layer. A GridBagLayout is used for displaying components on a panel. What is the exact problem you are trying to solve? I don't see how the behaviour you have achieved is any different than that of the original layered pane demo. – camickr Feb 23 '17 at 15:59
  • Edited the answer to explain the motivation. I could have used panels with transparent backgrounds and the GridBagLayout for each different layer but they just didn't want to work properly. – Mischa Feb 23 '17 at 16:07
  • `but they just didn't want to work properly` - that doesn't define the problem. You have two potential problems, 1) with the layered pane code or 2) with the constraints used for the GridBagLayout on each panel. Post a proper [mcve] that demonstrates the problem. From the image I can't tell which component is stacked or what the actual problem is. – camickr Feb 23 '17 at 16:24
  • Dear Robert, I may have misspoken in the previous comment. The screenshot I have posted is from the GUI I eventually have developed by using multiple panels with `GridBagLayout` for each of them, so that the initial goal has already been reached. However, while trying to circumvent the problem by extending `JLayeredPane` and playing around with it, I've encountered the above problem with the `JLabel`s. I'm just interested in understanding the reasons for this peculiar behaviour: Is there an error in my example or is there a fundamental reason why I cannot mix layers and `GridBagLayout`? – Mischa Feb 23 '17 at 16:33
  • 2
    It sooks that the first label is assigned the same position (x,y) as the next one, and is considered as totally occluded, and therefore is not painted. Thus the error appears in the layout phase, not during painting – Lesiak Feb 23 '17 at 17:19

2 Answers2

3
  • As @camickr has already said, JLayeredPane is irrelevant.
  • Take a look at GridBagLayout to create a board | Oracle Community. May be it helps.

    Darryl.Burke said:
    A column (or row) in a GridBagLayout is not well defined unless there is at least one component which occupies only that column (or row). All your rows have components spanning 2 columns.

screenshot

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;

public class LayeredPaneDemo2 extends JPanel implements ActionListener {
  private final Color[] layerColors = {
      Color.yellow, Color.magenta, Color.cyan,
      Color.red, Color.green, Color.blue };

  private final JLayeredPane layeredPane;
  private final List<JLabel> labels;

  private JButton update;

  public LayeredPaneDemo2() {
    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

    labels = new ArrayList<>();
    // JLayeredPane is not much related, it is a problem of how to use GridBagLayout.
    layeredPane = new LXLayeredPane();
    layeredPane.setPreferredSize(new Dimension(400, 410));
    layeredPane.setBorder(BorderFactory.createTitledBorder(
        "Click to change colors"));

    // Add several labels to the layered pane.
    layeredPane.setLayout(new GridBagLayout());

    for (int i = 0; i < layerColors.length; i++) {
      JLabel label = createColoredLabel("Test" + i, layerColors[i]);
      labels.add(label);
      layeredPane.add(label, new StackConstraints(i, gbc(i)));
    }
//     //TEST1: Create reference grid
//     GridBagConstraints c = new GridBagConstraints(
//       0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
//       GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
//     for (int i = 0; i < layerColors.length + 1; i++) {
//       c.gridx = i;
//       c.gridy = i;
//       layeredPane.add(Box.createRigidArea(new Dimension(20, 20)), c);
//     }
    //TEST2: Create reference grid >>>
    GridBagConstraints c = new GridBagConstraints(
        6, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
        GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
    for (int i = 0; i < layerColors.length; i++) {
      c.gridx = i;
      Component box = Box.createRigidArea(new Dimension(20, 20));
      ((JComponent) box).setBorder(BorderFactory.createLineBorder(Color.RED));
      layeredPane.add(box, c);
    }
    c.gridx = 6;
    for (int i = 0; i < layerColors.length; i++) {
      c.gridy = i;
      Component box = Box.createRigidArea(new Dimension(20, 20));
      ((JComponent) box).setBorder(BorderFactory.createLineBorder(Color.RED));
      layeredPane.add(box, c);
    }
    // <<<
    // Add control pane and layered pane to this JPanel.
    add(Box.createRigidArea(new Dimension(0, 10)));
    add(createControlPanel());
    add(Box.createRigidArea(new Dimension(0, 10)));
    add(layeredPane);
  }

  private GridBagConstraints gbc(int i) {
    return new GridBagConstraints(
        i, i, 2, 2, 0.0, 0.0, GridBagConstraints.CENTER,
        GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0);
  }

  // Create and set up a colored label.
  private JLabel createColoredLabel(String text, Color color) {
    JLabel label = new JLabel(text) {
      @Override protected void paintComponent(Graphics g) {
        g.setColor(new Color(100, 100, 100, 100));
        g.fillRect(0, 0, getWidth(), getHeight());
        super.paintComponent(g);
      }
    };
    label.setVerticalAlignment(JLabel.TOP);
    label.setHorizontalAlignment(JLabel.CENTER);
    label.setOpaque(true);
    label.setBackground(color);
    label.setForeground(Color.black);
    label.setBorder(BorderFactory.createLineBorder(Color.black));
    label.setPreferredSize(new Dimension(240, 240));
    return label;
  }

  // Create the control pane for the top of the frame.
  private JPanel createControlPanel() {
    update = new JButton("Update");
    update.addActionListener(this);
    update.setActionCommand("UPDATE");

    JPanel controls = new JPanel();
    controls.add(update);
    controls.setBorder(BorderFactory.createTitledBorder(
        "Choose Duke's Layer and Position"));
    return controls;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    Color prev = labels.get(labels.size() - 1).getBackground();

    for (int i = labels.size() - 1; i > 0; --i) {
      labels.get(i).setBackground(labels.get(i - 1).getBackground());
      labels.get(i).validate();
      labels.get(i).repaint();
    }
    labels.get(0).setBackground(prev);
    labels.get(0).validate();
    labels.get(0).repaint();
  }

  private static void createAndShowGUI() {
    JFrame frame = new JFrame("LayeredPaneDemo2");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JComponent newContentPane = new LayeredPaneDemo2();
    newContentPane.setOpaque(true);
    frame.setContentPane(newContentPane);

    frame.pack();
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        createAndShowGUI();
      }
    });
  }

}

class StackConstraints {
  public final int layer;
  public final Object layoutConstraints;

  public StackConstraints(int layer, Object layoutConstraints) {
    this.layer = layer;
    this.layoutConstraints = layoutConstraints;
  }
}

class LXLayeredPane extends JLayeredPane {
  @Override
  protected void addImpl(Component comp, Object constraints, int index) {
    int layer = 0;
    int pos = 0;
    Object constr = null;
    if (constraints instanceof StackConstraints) {
      layer = ((StackConstraints) constraints).layer;
      constr = ((StackConstraints) constraints).layoutConstraints;
    } else {
      layer = getLayer(comp);
      constr = constraints;
    }

    pos = insertIndexForLayer(layer, index);
    super.addImpl(comp, constr, pos);
    setLayer(comp, layer, pos);
    comp.validate();
    comp.repaint();
  }
}
aterai
  • 9,658
  • 4
  • 35
  • 44
1

is there a fundamental reason why I cannot mix layers and GridBagLayout?

That is my point. I don't believe you need to customize JLayeredPane.

A layered pane is used to layer panels.

Then each individual panel can have its own layout manager whether it be GridBagLayout, FlowLayout or whatever.

For example, try updating the loop that creates the labels in the LayeredPaneDemo:

JLabel label = null; 

for (int i = 0; i < layerStrings.length; i++) {
    //JLabel label = createColoredLabel(layerStrings[i], layerColors[i], origin);
    label = createColoredLabel(layerStrings[i], layerColors[i], origin);
    layeredPane.add(label, new Integer(i));
    origin.x += offset;
    origin.y += offset;
}

label.setLayout( new GridBagLayout() );
label.add(new JCheckBox("Check Me"), new GridBagConstraints() );

The above code will just add a checkbox to the label using the default constraints which means the checkbox will be centered within the label.

I am not aware of any reason you can't use a different layout manager on each panel in the layered pane. If the layout isn't what you expect, then I would guess your GridBagConstrainsts are not correct.

camickr
  • 321,443
  • 19
  • 166
  • 288