滚动的 JLayer 不表现为它应该替换的滚动的 JPanel
Scrolled JLayer not behaving as a scrolled JPanel which it is supposed to replace
我想用 JLayer 装饰 JPanel,但我不明白为什么这样做会弄乱这个在 JScrollPane 中布局的面板。装饰组件本应作为替代品,但在这种情况下似乎不起作用。
以下代码创建两个等效的 JPanel 并将它们放入另一个带有 CardLayout 的面板中(因此您可以使用按钮在它们之间切换)。唯一的区别是,在一种情况下,面板装饰有 JLayer。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class JLayerScroll extends JFrame {
public JLayerScroll() {
setTitle("Jumpy border");
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
createGui();
setSize(400, 200);
setLocationRelativeTo(null);
}
private void createGui() {
JPanel mainPanel = new JPanel(new CardLayout());
// two panels ("label panels"), first one decorated, second one not
mainPanel.add(createSingleScrolledComponent(new JLayer<JPanel>(createLabelPanel(), new LayerUI<JPanel>())), WITH_JLAYER);
mainPanel.add(createSingleScrolledComponent(createLabelPanel()), WITHOUT_JLAYER);
add(mainPanel);
createButtons(mainPanel);
}
private JPanel createSingleScrolledComponent(Component component) {
GridBagConstraints gbc;
JScrollPane scroll;
JPanel panel = new JPanel(new GridBagLayout());
scroll = new JScrollPane(component);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.0d;
gbc.weighty = 1.0d;
panel.add(scroll, gbc);
return panel;
}
private JPanel createLabelPanel() {
GridBagConstraints gbc;
JPanel panel = new JPanel(new GridBagLayout());
JPanel entry = new JPanel(new GridBagLayout());
entry.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.red));
JLabel label = new JLabel("Some input:");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
entry.add(label, gbc);
JTextField field = new JTextField(20);
field.setMinimumSize(new Dimension(field.getPreferredSize().width, field.getMinimumSize().height));
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
entry.add(field, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
entry.add(Box.createHorizontalGlue(), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
panel.add(entry, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0d;
panel.add(Box.createVerticalGlue(), gbc);
return panel;
}
private static final String WITH_JLAYER = "with-jlayer";
private static final String WITHOUT_JLAYER = "no-jlayer";
private void createButtons(final JPanel mainPanel) {
GridBagConstraints gbc;
JButton button;
JPanel actionsPanel = new JPanel(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
actionsPanel.add(Box.createHorizontalGlue(), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
button = new JButton("With JLayer");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout layout = (CardLayout) mainPanel.getLayout();
layout.show(mainPanel, WITH_JLAYER);
}
});
actionsPanel.add(button, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
button = new JButton("Without JLayer");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout layout = (CardLayout) mainPanel.getLayout();
layout.show(mainPanel, WITHOUT_JLAYER);
}
});
actionsPanel.add(button, gbc);
add(actionsPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JLayerScroll().setVisible(true);
}
});
}
}
如果你注意这两种情况下的红色边框,你会注意到在一种情况下它跨越了整个可用的水平线space(正如预期的那样),而在另一种情况下,它只跨越了整个可用的水平线space围绕可见组件(标签和文本字段)。
为什么会发生这种情况以及如何实现与未修饰情况相同的行为(跨越所有可用的水平 space)?
The only difference is that in one case the panel is decorated with a JLayer.
JPanel
和 JLayer
之间的区别在于 JLayer
实现了 Scrollable
接口,而 JPanel
没有。
getScrollableTracksViewportWidth()
方法控制添加到视口的组件是否应以其首选大小或视口宽度显示。 JLayer 委托给 JPanel,但由于 JPanel 未实现 Scrollable 接口,因此 JLayer 实现将 return "false",这意味着组件应以其首选宽度显示。
因此,解决此问题的一种方法是为 JLayer
:
使用包装面板
//scroll = new JScrollPane(component);
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add( component );
scroll = new JScrollPane(wrapper);
现在添加到滚动窗格视口的组件在这两种情况下都是 JPanel,因此它们的行为方式应该相同。
我想用 JLayer 装饰 JPanel,但我不明白为什么这样做会弄乱这个在 JScrollPane 中布局的面板。装饰组件本应作为替代品,但在这种情况下似乎不起作用。
以下代码创建两个等效的 JPanel 并将它们放入另一个带有 CardLayout 的面板中(因此您可以使用按钮在它们之间切换)。唯一的区别是,在一种情况下,面板装饰有 JLayer。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
public class JLayerScroll extends JFrame {
public JLayerScroll() {
setTitle("Jumpy border");
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
createGui();
setSize(400, 200);
setLocationRelativeTo(null);
}
private void createGui() {
JPanel mainPanel = new JPanel(new CardLayout());
// two panels ("label panels"), first one decorated, second one not
mainPanel.add(createSingleScrolledComponent(new JLayer<JPanel>(createLabelPanel(), new LayerUI<JPanel>())), WITH_JLAYER);
mainPanel.add(createSingleScrolledComponent(createLabelPanel()), WITHOUT_JLAYER);
add(mainPanel);
createButtons(mainPanel);
}
private JPanel createSingleScrolledComponent(Component component) {
GridBagConstraints gbc;
JScrollPane scroll;
JPanel panel = new JPanel(new GridBagLayout());
scroll = new JScrollPane(component);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.0d;
gbc.weighty = 1.0d;
panel.add(scroll, gbc);
return panel;
}
private JPanel createLabelPanel() {
GridBagConstraints gbc;
JPanel panel = new JPanel(new GridBagLayout());
JPanel entry = new JPanel(new GridBagLayout());
entry.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.red));
JLabel label = new JLabel("Some input:");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
entry.add(label, gbc);
JTextField field = new JTextField(20);
field.setMinimumSize(new Dimension(field.getPreferredSize().width, field.getMinimumSize().height));
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
entry.add(field, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
entry.add(Box.createHorizontalGlue(), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
panel.add(entry, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0d;
panel.add(Box.createVerticalGlue(), gbc);
return panel;
}
private static final String WITH_JLAYER = "with-jlayer";
private static final String WITHOUT_JLAYER = "no-jlayer";
private void createButtons(final JPanel mainPanel) {
GridBagConstraints gbc;
JButton button;
JPanel actionsPanel = new JPanel(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0d;
actionsPanel.add(Box.createHorizontalGlue(), gbc);
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
button = new JButton("With JLayer");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout layout = (CardLayout) mainPanel.getLayout();
layout.show(mainPanel, WITH_JLAYER);
}
});
actionsPanel.add(button, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
button = new JButton("Without JLayer");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
CardLayout layout = (CardLayout) mainPanel.getLayout();
layout.show(mainPanel, WITHOUT_JLAYER);
}
});
actionsPanel.add(button, gbc);
add(actionsPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JLayerScroll().setVisible(true);
}
});
}
}
如果你注意这两种情况下的红色边框,你会注意到在一种情况下它跨越了整个可用的水平线space(正如预期的那样),而在另一种情况下,它只跨越了整个可用的水平线space围绕可见组件(标签和文本字段)。
为什么会发生这种情况以及如何实现与未修饰情况相同的行为(跨越所有可用的水平 space)?
The only difference is that in one case the panel is decorated with a JLayer.
JPanel
和 JLayer
之间的区别在于 JLayer
实现了 Scrollable
接口,而 JPanel
没有。
getScrollableTracksViewportWidth()
方法控制添加到视口的组件是否应以其首选大小或视口宽度显示。 JLayer 委托给 JPanel,但由于 JPanel 未实现 Scrollable 接口,因此 JLayer 实现将 return "false",这意味着组件应以其首选宽度显示。
因此,解决此问题的一种方法是为 JLayer
:
//scroll = new JScrollPane(component);
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add( component );
scroll = new JScrollPane(wrapper);
现在添加到滚动窗格视口的组件在这两种情况下都是 JPanel,因此它们的行为方式应该相同。