问题
I am trying to place some JPanel
s one on top of the other, completely overlapping.
I use JLayeredPane
to have them in different "depth" so I can change the depth and opacity to see a certain panel "under" another.
Here is a small test I did which is working properly:
public class LayeredCardPanel extends JPanel {
private static final String BLUE_PANEL = "blue ";
private static final String RED_PANEL = "red ";
private static final String GREEN_PANEL = "green";
private String[] panelNames = { BLUE_PANEL, RED_PANEL, GREEN_PANEL };
private Color[] panelColors = { Color.BLUE, Color.RED, Color.GREEN };
private List<JPanel> panels = new ArrayList<>();
private final int TOP_POSITION = 30;
private static final int PANELS_FIRST_POS = 10;
private JLayeredPane layeredPane;
public LayeredCardPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(createControlPanel());
layeredPane = new JLayeredPane();
////////////////////////////////////////////////////////////////
//setting layout here results in all grey, non functioning panel.
//layeredPane.setLayout(new CardLayout(0, 0));
//////////////////////////////////////////////////////////////
add(layeredPane);
//adding 3 panel
for (int i = 0; i < panelNames.length; i++) {
JPanel panel = new JPanel();
panel.setBackground(panelColors[i]);
layeredPane.add(panel);
layeredPane.setLayer(panel, PANELS_FIRST_POS + i);
panels.add(panel);
}
////////////////////////////////////////////////////////////
//setting the card here, after adding panels, works fine
layeredPane.setLayout(new CardLayout(0, 0));
//////////////////////////////////////////////////////////
}
The test result looks like this:
As you can see in between the ////// commented line, this test works fine only if I set CardLayout
to the JLayeredPane
after I add the JPanel
s to it.
It seems to me that the JPanel
s are added to the JLayeredPane
using the default layout manager. The layout (setting and changing bounds to fill the JLayeredPane
) is done by the later-applied CardLayout
.
My question is: although this is working as I need, the solution (using one layout manager to add, and then replacing it to achieve the layout I want) does not look an elegant solution. I am looking for better ways to do it.
Here is the whole SSCE :
public class LayeredCardPanel extends JPanel {
private static final String BLUE_PANEL = "blue ";
private static final String RED_PANEL = "red ";
private static final String GREEN_PANEL = "green";
private String[] panelNames = { BLUE_PANEL, RED_PANEL, GREEN_PANEL };
private Color[] panelColors = { Color.BLUE, Color.RED, Color.GREEN };
private List<JPanel> panels = new ArrayList<>();
private final int TOP_POSITION = 30;
private static final int PANELS_FIRST_POS = 10;
private JLayeredPane layeredPane;
public LayeredCardPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(createControlPanel());
layeredPane = new JLayeredPane();
add(layeredPane);
//add 3 panel
for (int i = 0; i < panelNames.length; i++) {
JPanel panel = new JPanel();
panel.setBackground(panelColors[i]);
layeredPane.add(panel);
layeredPane.setLayer(panel, PANELS_FIRST_POS + i);
panels.add(panel);
}
layeredPane.setLayout(new CardLayout());
}
private JPanel createControlPanel() {
ActionListener aListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof JButton ) {
moveToTop(((JButton) e.getSource()).getActionCommand() );
}
}
};
JPanel controls = new JPanel();
controls.setLayout(new BoxLayout(controls, BoxLayout.Y_AXIS));
JButton blueViewBtn = new JButton(BLUE_PANEL);
blueViewBtn.setActionCommand(BLUE_PANEL);
blueViewBtn.addActionListener(aListener);
controls.add(blueViewBtn);
JButton redViewBtn = new JButton(RED_PANEL);
redViewBtn.setActionCommand(RED_PANEL);
redViewBtn.addActionListener(aListener);
controls.add(redViewBtn);
JButton greenViewBtn = new JButton(GREEN_PANEL);
greenViewBtn.setActionCommand(GREEN_PANEL);
greenViewBtn.addActionListener(aListener);
controls.add(greenViewBtn);
return controls;
}
private void moveToTop(String panelName) {
for(int i = 0; i < panelNames.length; i++) {
if(panelNames[i].equals(panelName)) {
layeredPane.setLayer(panels.get(i),TOP_POSITION);
} else {
layeredPane.setLayer(panels.get(i), PANELS_FIRST_POS + i);
}
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Layered Card Panel Simulation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new LayeredCardPanel();
newContentPane.setPreferredSize(new Dimension(300,100));
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
});
}
}
回答1:
Basically you are NOT using a CardLayout. Your code would work even if you don't have the setLayout(...)
statement.
When you use a CardLayout:
- The layout manager can only track the cards added after you have set the layout manager.
- only one component is ever displayed at a time, which you don't want since you want to play with panel transparency
Edit:
When you set the CardLayout AFTER the panels are added to the layered pane they are NOT handled by the CardLayout. As a result all panel remain visible. Apparently all that happens is that the CardLayout will set the size of each panel to fill the space available so painting appears to work correctly.
When I modify your SSCCE to set the CardLayout BEFORE adding the panels then the CardLayout does manage the panels. So the CardLayout invokes setVisible(false) on all the panels except the first panel added, which in the SSCCE is the blue panel. So you will only ever see the blue panel even though you try to move different panels to the front.
This is easily verified by the following change to the SSCCE:
layeredPane.setLayer(panel, PANELS_FIRST_POS + i);
System.out.println(panel.isVisible());
So yes, using a CardLayout is really a hack and is not the way the CardLayout was intended to be used or the way a JLayeredPane was intended to be used.
A JLayeredPane is really meant to be used with a null layout meaning you are responsible for setting the size of each component. For a better solution I would suggest that you need to add a ComponentListener
to the layered pane. Then you should handle the componentResized(...)
event. Then you would iterate through all the components added to the layered pane and set the size of each component equal to the size of the layered pane.
回答2:
The solution I applied is the following: I changed the contractor so layeredPane uses custom layout manager Layout()
:
public LayeredCardPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(createControlPanel());
layeredPane = new JLayeredPane();
layeredPane.setLayout(new Layout());
add(layeredPane);
//add 3 panel
for (int i = 0; i < panelNames.length; i++) {
JPanel panel = new JPanel();
panel.setBackground(panelColors[i]);
layeredPane.add(panel);
layeredPane.setLayer(panel, PANELS_FIRST_POS + i);
panels.add(panel);
}
}
Layout()
extends CardLayout
overriding addLayoutComponent
to do nothing:
class Layout extends CardLayout{
@Override
public void addLayoutComponent(String name, Component comp) {
// override to do nothing
}
}
来源:https://stackoverflow.com/questions/35769412/java-swing-combine-effects-of-cardlayout-and-jlayeredpane