Java Swing: combine effects of CardLayout and JLayeredPane

两盒软妹~` 提交于 2019-12-11 10:37:23

问题


I am trying to place some JPanels 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 JPanels to it. It seems to me that the JPanels 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:

  1. The layout manager can only track the cards added after you have set the layout manager.
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!