【问题标题】:Overlay panel (above another)覆盖面板(在另一个之上)
【发布时间】:2012-01-09 16:52:35
【问题描述】:

我正在学习如何使用 Swing,发现自己的任务相当艰巨。

我想要完成的工作:我想在左侧(比如说 100 像素宽度)和第二个面板(称为内容面板)有面板(称为菜单面板),它占据了其余可用位置。

在菜单面板中有 3 个按钮。当我按下它们时,菜单面板(内容面板上方)的右侧应该出现第二个菜单面板(子菜单)(它应该从按下的按钮中间开始)。

这可能很难理解,所以我创建了一个简单的草稿:

我尝试了 JLayeredPane,但在调整窗口大小时出现问题(分层窗格中的元素没有调整大小)。

【问题讨论】:

  • 你可以试试 JPopupMenu 的子类。另外,检查 JComboBox 的源代码,看看他们如何使用那里的弹出菜单。

标签: java swing


【解决方案1】:

JLayeredPane 错过了LayoutManager 的实现,您必须手动setPreferredSizesetBounds 来调整/放置JComponents

有一种可能的解决方法,您可以将ComponentListener 添加到JFrame,然后在componentResized(ComponentEvent e) 上,您可以将JComponent(s) 调整大小/替换为所需的Bounds

例如

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

public class LayeredPaneWithOverlap {

    private JTextArea textArea = new JTextArea(2, 10);
    private JPanel textPanel = new JPanel(new BorderLayout());
    private JTable table = new JTable(30, 5);
    private JScrollPane scroll = new JScrollPane(table);
    private JLayeredPane layer = new JLayeredPane();
    private JFrame frame = new JFrame("Frame with resiziable JLayeredPane");

    public void makeUI() {
        textArea.setBorder(new LineBorder(Color.DARK_GRAY));
        textArea.setText("Frame with resiziable JLayeredPane");
        textPanel.setOpaque(false);
        textPanel.add(textArea, BorderLayout.NORTH);
        Font font = textArea.getFont();
        FontMetrics fontMetrics = textArea.getFontMetrics(font);
        int h = fontMetrics.getHeight() + frame.getInsets().top + 
                textPanel.getInsets().top + textArea.getInsets().top
                + textArea.getInsets().bottom;
        scroll.setBounds(0, h, 400, 300);
        layer.add(textPanel, new Integer(2));
        layer.add(scroll, new Integer(1));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 400);
        frame.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {

                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        resizeAll();
                    }
                });
            }
        });
        frame.setLocationRelativeTo(null);
        frame.add(layer);
        resizeAll();
        frame.setVisible(true);
    }

    void resizeAll() {
        Insets insets = frame.getInsets();
        int w = frame.getWidth() - insets.left - insets.right;
        int h = frame.getHeight() - insets.top - insets.bottom;
        textPanel.setSize(w, h);
        scroll.setSize(w, h - scroll.getY());
        layer.revalidate();
        layer.repaint();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new LayeredPaneWithOverlap().makeUI();
            }
        });
    }
}

【讨论】:

  • 哇。这很好用。但我有一个问题。为什么在这一行 textPanel.setSize(w, h);您是否将 textPanel 高度设置为框架高度?并且 textPanel 不是屏幕高度(它的高度保持不变)。
  • 关于将 JComponents 填充到 JFrame 中,您可以通过 uisng setSize/setBounds 使用 Insets,您可以设置相对于父级的所需/预期大小位置
【解决方案2】:

您可以为分层窗格设置一个布局管理器,javax.swing.OverlayLayout 使用全部可用空间并允许调整大小。

JLayeredPane layer = new JLayeredPane();
layer.setLayout(new OverlayLayout(layer));

您可能不希望子菜单占据整个空间。为了避免它,你可以覆盖它的 get...size-methods。或者您可以添加第二个 LayeredPane(因为它是透明的,并且是 layoutmanager),设置一个普通的 BoxLayout 并使用一个间隔。

JPanel normalContents = new JPanel();
layer.add(normalContents, JLayeredPane.DEFAULT_LAYER);
JLayeredPane subMenuAuxiliaryLayer = new JLayeredPane()
subMenuAuxiliaryLayer.setLayout(new BoxLayout(subMenuAuxiliaryLayer, BoxLayout.LINE_AXIS));
layer.add(subMenuAuxiliaryLayer, JLayeredPane.PALETTE_LAYER);
JPanel submenuContents = new JPanel();
subMenuAuliliaryLayer.add(submenuContents);
subMenuAuxiliaryLayer.add(Box.createHorizontalGlue());

【讨论】:

    【解决方案3】:
    contentPanel.setLayout(null); // Absolute positioning of children.
    
    @Override
    public void actionPerformed(ActionEvent evt) {
        final JButton btn = (JButton) evt.getSource();
        final int buttonY = btn.getY(); // Must be final for usage in new Runnable object.
    
        SwingUtilities.invokeLater(new Runnable() { // Return fast from event handling.
    
            @Override
            public void run() {
                JPanel child = new JPanel();
                child.setBackground(Color.RED); // So we'll see it.
                child.setBounds(0, buttonY, 100, 300);
                contentPanel.removeAll(); // Clear content panel of prior additions.
                contentPanel.add(child); // Add a new panel.
                contentPanel.repaint(10L);
            }
        });
     }
    

    【讨论】:

    • 如果我使用的是 ie。内容面板中的组布局?我正在考虑将此子菜单作为所有其他内容之上的附加面板。
    • 然后在 contentPanel 中有两个分层的窗格,上面的一个用于菜单。
    【解决方案4】:

    JLayeredPane 默认工作,没有布局管理器,这意味着您使用的是绝对定位并且没有调整大小。您可以添加一个调整大小的侦听器,并根据您认为合适的方式从代码中调整内部组件的位置和大小。

    如果您不想通过代码执行此操作,您将需要一个布局管理器,没什么特别的,只是在调整大小时填充容器。但事情是这样的……如果你添加一个布局管理器,它会将组件布局为好像它们都在一个层中,但大多数布局管理器不会重叠它们的子级,所以它们是无用的。您唯一可以使用的是 OverlayLayout - 它还可以调整子项的大小。但是使用带有 JLayeredPane 的 OverlayLayout 是多余的。您可以将 OverlayLayout 与 JPanel 一起使用。所以,是的,JLayeredPane 有点没用。我建议改用带有 OverlayLayout 的 JPanel。

    以下是设置方法,以便您可以很好地控制几乎所有重叠的 UI 场景:使用带有 OverlayLayout 的 JPanel,为每一层设置一个单独的透明 JPanel。通过这种方式,您可以通过为每个窗格设置不同的布局管理器(包括必要时的绝对定位)来组合不同层上的各种 LayoutManager。然后在代表图层的面板中添加可见组件。不要将它们直接添加到 OverlayLayout 面板。只需确保您用作图层的所有 JPanel 都将 setAlignmentX 和 Y 设置为居中 (0.5f),以便它们在调整大小时填充整个 OverlayLayout 面板。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-26
      • 1970-01-01
      • 1970-01-01
      • 2015-06-08
      • 1970-01-01
      • 1970-01-01
      • 2011-12-14
      相关资源
      最近更新 更多