【问题标题】:Newbie JLayeredPane issue新手 JLayeredPane 问题
【发布时间】:2012-07-16 17:44:21
【问题描述】:

我只是无法通过 JLayeredPanes 上的第一格。 (请参阅我的original question of yesterday。我一直在研究 JLayeredPane 教程和 API。这些教程与我最终尝试制作的内容有些不同。

回到第一点,我采用了 Oracle 的 JFrame 示例并对其进行了修改以包含分层窗格。

代码如下:

package components;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/* FrameDemo.java requires no other files. */
public class FrameDemo {
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("FrameDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel mainLayer = new JPanel(new BorderLayout());
        mainLayer.setPreferredSize(new Dimension(640, 480));
        frame.setContentPane(mainLayer);
        frame.getLayeredPane().add(mainLayer, JLayeredPane.DEFAULT_LAYER, 0);

        JLabel emptyLabel = new JLabel("LABEL");
        emptyLabel.setPreferredSize(new Dimension(320, 240));
        mainLayer.add(emptyLabel, BorderLayout.NORTH);

        JPanel subLayer = new JPanel(new BorderLayout());
        JLabel subLabel = new JLabel("SUBLABEL");
        subLabel.setPreferredSize(new Dimension( 200, 100));
        subLabel.setBackground(Color.YELLOW);
        subLayer.add(subLabel, BorderLayout.SOUTH);
        subLayer.setVisible(true);
        subLabel.setVisible(true);
        frame.getLayeredPane().add(subLayer, JLayeredPane.PALETTE_LAYER, 0);


        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

为什么它不起作用? IOW,为什么子标签不显示?它比主层更高。

一个想法是为什么我将 mainLayer 添加到内容窗格和分层窗格中?如果我不这样做,什么都不会出现。即,通过注释掉这一行,我只会得到一个空白框。

//        frame.setContentPane(mainLayer);

显然,我不明白一些事情。但它是什么?

我应该补充一点,这个简单的演示可以在没有分层窗格的情况下完成。但我的最终目标是拥有一个可以通过编程方式打开和关闭的层。但我什至无法让这个简单的案例起作用。如果我能克服这个困难,我想剩下的会更容易。

附录:

下面的代码说明了我想要实现的目标,它与下面的 TrashGod 设置非常相似,并且可以正常工作。有一个 JLayeredPane 具有一个常量层(在 Integer(0) 处分层)和一个浮动层,最初在 Integer(-1) 处分层,但可通过 Integer(-1) 层和 Integer(1) 之间的 F7 和 F8 键击切换层,从而允许它浮动在常量层之上或之下。

package components;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


/* MyLayeredPaneDemo.java requires no other files. */
public class MyLayeredPaneDemo {
    private JFrame frame;
    private JLayeredPane mainPanel;
    private JPanel constantLayer;
    private JPanel floatingLayer;
    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private MyLayeredPaneDemo() {}
    private void createAndShowGUI() {
        //Create and set up the window.
        this.frame = new JFrame("MyLayeredPaneDemo");
        this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.frame.setPreferredSize(new Dimension(640, 480));
        mainPanel = new JLayeredPane();
        constantLayer = new JPanel(new BorderLayout(0,0));
        floatingLayer = new JPanel(new BorderLayout(0,0));
//        constantLayer.setPreferredSize();
        constantLayer.setOpaque(true);
        constantLayer.setBackground(Color.BLUE);


        JLabel constantLabel = new JLabel("MAIN LAYER");
        constantLayer.setPreferredSize(new Dimension(640, 480));
        constantLayer.add(constantLabel, BorderLayout.CENTER);

        JLabel subLabel = new JLabel("SUB LAYER");
        floatingLayer.setBackground(Color.YELLOW);
        floatingLayer.add(subLabel, BorderLayout.SOUTH);
        floatingLayer.setOpaque(true);
        floatingLayer.setVisible(true);
        floatingLayer.setVisible(true);
        subLabel.setBackground(Color.YELLOW);

        mainPanel.add(constantLayer, new Integer(0), 0);
        constantLayer.setBounds(0,0,640,480);
        mainPanel.add(floatingLayer, new Integer(-1), 0);
        floatingLayer.setBounds(100, 360, 300, 90 );

        frame.add(mainPanel, BorderLayout.CENTER);

        //Display the window.
        mapKeyToAction(frame.getRootPane(), 
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
                KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0),
                "Hide Layer", 
                new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("F7 pressed");
                        mainPanel.setLayer(floatingLayer, new Integer(-1));
                    }       
                }); 
        mapKeyToAction(frame.getRootPane(), 
                JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
                KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),
                "Show Layer", 
                new AbstractAction() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("F8 pressed");
                        mainPanel.setLayer(floatingLayer, new Integer(1));
                    }       
                }); 
        frame.pack();
        frame.setVisible(true);
        frame.getRootPane().setFocusable(true);
        boolean ok = frame.getRootPane().requestFocusInWindow();
        System.out.println("focus ok: " + ok);

    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new MyLayeredPaneDemo().createAndShowGUI();
            }
        });
    }

    private static void mapKeyToAction(JComponent component, 
            int whichMap, KeyStroke keystroke,String key, Action action) {
        component.getInputMap(whichMap).put(keystroke, key);
        component.getActionMap().put(key, action);
    }

}

但是,在我的实际情况下,我无法让它发挥作用。两者之间的区别在于,在这里,我的 JLayeredPane 由 Frame 拥有,而在我的实际应用程序中,我希望 JLayeredPane 由 JPanel 拥有,因为 Frame 包含层次结构中的某些级别,其大小为由其父级中的 GridBagLoyout 设置,因此在调用其构造函数时大小是未知的,因此很难调用我需要对 JLayeredPane 的子级执行的 setBounds()。

进一步的附录。我知道 Oracle 教程提到了 case where Layouts rather than absolute positioning is used with a JLayeredPane。这种情况和我的不同之处在于,在我的情况下,层在不同层上占据相同的水平空间,而在这种情况下,不同层上的组件占据不同的水平空间。就好像我们需要一个 3D 布局管理器一样!

【问题讨论】:

  • 进一步评论。我看过 Oracle 的 RootLayeredPaneDemo 教程。这是一个非常混乱的演示。框架将其分层窗格传递给主类,该主类是一个 JPanel,它成为框架的内容窗格。然后在此 JPanel 的构造函数中将几个组件添加到该分层窗格中,即使主 (JPanel) 类中没有包含分层窗格。我上面的代码似乎遵循 RootLayeredPaneDemo 的包含模型,但事实并非如此。事物被实例化、添加到容器和可见的顺序是否重要?
  • 我没有时间详细分析,但我突然想到的一件事是,您将 maimLayer 设置为内容,然后将其添加到框架分层窗格中。一个组件只能有一个父级,这有效地删除了内容 psne
  • 这个评论说的很对。这让我一分钱一分货。不幸的是,我仍然遇到 JLayeredPanes 的问题,我将在此处或稍后在新问题中进行描述。

标签: java swing jlayeredpane


【解决方案1】:

我想出的解决方案是实现 ComponentListener 并捕获组件调整大小事件。此时,您可以获得容器的实际边界并使用它来设置 JPanel 层的实际边界,这些边界始终与包含它们的组件的边界保持某种固定关系。它有效。

我相信 Trashgod 的解决方案也可以,但我没有尝试过。

【讨论】:

    【解决方案2】:

    “默认情况下,分层窗格没有布局管理器。”—How to Use Layered Panes

    附录:我需要避免使用Frame的分层窗格,而是在窗口中添加分层窗格

    是的,The Root PaneJRootPane 的一个实例,其中包含一个 JLayeredPane。特别是,“分层窗格包含菜单栏和内容窗格,并启用其他组件的 Z 排序。”

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class FrameDemo {
    
        private static void createAndShowGUI() {
            JFrame frame = new JFrame("FrameDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            JLayeredPane mainLayer = new JLayeredPane();
            frame.add(mainLayer, BorderLayout.CENTER);
    
            JLabel label = new JLabel("LABEL", JLabel.CENTER);
            label.setBounds(100, 100, 200, 100);
            label.setOpaque(true);
            label.setBackground(Color.cyan);
            mainLayer.add(label, 1);
    
            JPanel subLayer = new JPanel(new BorderLayout());
            JLabel subLabel = new JLabel("SUBLABEL", JLabel.CENTER);
            subLabel.setBounds(20, 20, 200, 100);
            subLabel.setOpaque(true);
            subLabel.setBackground(Color.yellow);
            subLayer.add(subLabel, BorderLayout.SOUTH);
            mainLayer.add(subLabel, 2);
    
            frame.pack();
            frame.setSize(320, 240);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    createAndShowGUI();
                }
            });
        }
    }
    

    【讨论】:

    • 感谢这个有趣的例子。与我的主要区别似乎是您忽略了框架的 JLayeredPane 而是将新的 JLayeredPane 添加到框架中,然后将所有组件添加到分层窗格而不是框架中,并具有适当的索引。这可能值得探索。
    • 我想我在我的应用程序中有一些工作,我将内容窗格单独放置并引入了一个 JLayeredPane,其内容可切换到高于或低于默认级别的图层。这似乎有效,直到我将它部署到我的应用程序中,由于外部线程的活动,它会不时更新。令我大吃一惊的是,内容窗格上的内容更新有时会覆盖分层窗格上的内容,即使分层窗格位于比内容窗格更高的层上。
    • 我认为,我需要做的是避免使用 Frame 的分层窗格,而是将分层窗格添加到有时要覆盖其内容的窗口。此窗口中的所有内容,无论在任何层上,都需要包含在分层窗格中。似乎要有效地使用 LayeredPanes,每个想要覆盖或被其他组件覆盖的组件都需要包含在分层窗格中。你同意吗?
    • 是的;我上面已经详细说明了。展望未来,你能扩大你的目标吗?
    • 嵌入分层窗格时,不要设置preferred size,就像演示经常做的那样;覆盖getPreferredSize() 并使用尊重首选大小的布局。我经常为此使用GridLayoutGridBagLayout 与默认约束类似。
    猜你喜欢
    • 2012-05-11
    • 2013-07-13
    • 2016-12-14
    • 1970-01-01
    • 2010-12-08
    • 2011-08-13
    • 2010-12-20
    • 2010-11-05
    • 1970-01-01
    相关资源
    最近更新 更多