问题描述
我有 JPanel
,名为 pnlMain
,布局设置为 BorderLayout
。在面板内部有三个 JPanel
添加到 PAGE_START
, CENTER
,和 PAGE_END
。现在,我的要求是如果单击设置按钮,它将在 pnlMain
上方显示透明的 JPanel
。这个透明面板将包含一个不透明的,较小的居中面板,它将包含设置内容。
I have a JPanel
called pnlMain
with a layout set to BorderLayout
. Inside the panel are three JPanel
added to PAGE_START
, CENTER
, and PAGE_END
. Now, my requirement is if the Settings button is clicked, it will display a transparent JPanel
above pnlMain
. This transparent panel will then contain an opaque, smaller, centered panel, that will contain the settings stuff.
我知道我可以使用 JLayeredPane来做到这一点
,但是要查看它说你只能使用绝对定位放置不同深度的组件,我知道这是非常不鼓励的。
I know I can do this using JLayeredPane
, but looking at the tutorial it says that you can only put components of different depth using absolute positioning which I'm aware is highly discouraged.
还有其他方法可以不使用绝对定位?
Is there some other way to do this without using absolute positioning?
推荐答案
您可以使用父框架的玻璃窗格,这将允许您向其添加组件似乎覆盖了主要内容。
You can use the glass pane of the parent frame, which will allow you to add components to it that will appear to overlaid over the main content.
基本上,我会创建一个 JPanel
并将其设置为透明( setOpaque(假)
)。我会将它的布局管理器设置为类似 GridBagLayout
(因为它将使用子组件的首选大小并将其自动置于其父容器中)。
Basically, I would, create a JPanel
and set it to be transparent (setOpaque(false)
). I would set it's layout manager to something like GridBagLayout
(as it will use the preferred size of the child component and center it automatically within it's parent container).
在这个面板上,我会添加设置
面板。
Onto this panel I would then add the Settings
panel.
最后,我将父框架的玻璃窗格设置为第一个(支持)窗格并使其可见。
Finally, I would set the parent frame's glass pane to the first (backing) pane and make it visible.
frame.getRootPane().setGlassPane(backingPane); // Or similar
看一下
更新
如果您不能自己使用顶层框架的玻璃窗格,那么您需要伪造它。
If you can't use the glass pane of the top level frame yourself, then you need to fake it.
此示例基本上使用 JLayeredPane
支持 GridBagLayout
This example basically uses a JLayeredPane
backed by a GridBagLayout
如果将 MouseListener
和 KeyListener
添加到背景窗格,则可以使用转到子组件的事件。 / p>
If you add a MouseListener
and KeyListener
to the background pane you can consume events going to the child components.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class FloatingPane {
public static void main(String[] args) {
new FloatingPane();
}
public FloatingPane() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final WorkPane workPane = new WorkPane();
JButton settings = new JButton("Settings");
settings.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
workPane.toggleSettings();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(workPane);
frame.add(settings, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class WorkPane extends JLayeredPane {
private final BackingPane backingPane;
public WorkPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(createLabel("Center", Color.BLUE), gbc);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.VERTICAL;
add(createLabel("Left", Color.RED), gbc);
gbc.gridx = 2;
add(createLabel("Right", Color.GREEN), gbc);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.BOTH;
backingPane = new BackingPane();
backingPane.add(new SettingsPane());
backingPane.setVisible(false);
add(backingPane, gbc);
setLayer(backingPane, DEFAULT_LAYER + 1);
}
public void toggleSettings() {
backingPane.setVisible(!backingPane.isVisible());
}
protected JLabel createLabel(String text, Color bg) {
JLabel label = new JLabel(text);
label.setHorizontalAlignment(JLabel.CENTER);
label.setOpaque(true);
label.setBackground(bg);
return label;
}
}
public class BackingPane extends JPanel {
public BackingPane() {
setLayout(new GridBagLayout());
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(128, 128, 128, 192));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class SettingsPane extends JPanel {
public SettingsPane() {
setBorder(new EmptyBorder(10, 10, 10, 10));
add(new JLabel("Settings"));
}
}
}
另一种解决方案可能是通过拍摄当前面板的快照并使用 CardLayout
伪造整个玻璃窗格,翻转到设置窗格,使用快照拍摄作为背景图像设置窗格(然后可以将效果应用于灰度缩放和模糊)
Another solution might be to fake the entire glass pane by taking a snap shot of the current panel and using a CardLayout
, flip to the settings pane, using the snap shot as the backgound image for the settings pane (which can could then apply effects to like gray scaling and bluring)
这篇关于使用BorderLayout将JPanel浮动到JPanel上方的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!