滚动的JLayer不能表现为它应该替换的滚动JPanel

时间:2018-03-29 11:40:31

标签: java swing jscrollpane jlayer

我想用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);
            }
        });
    }

}

如果你注意两种情况下的红色边框,你会注意到在一种情况下它跨越整个可用的水平空间(如预期的那样),而在另一种情况下,它只跨越可见的组件(标签和文本域)。

Jumpy Border

为什么会发生这种情况?如何实现与未修饰案例相同的行为(跨越所有可用的水平空间)?

1 个答案:

答案 0 :(得分:1)

  

唯一的区别是,在一种情况下,面板上装饰有JLayer。

JPanelJLayer之间的区别在于JLayer实现了Scrollable接口,而JPanel却没有。

getScrollableTracksViewportWidth()方法控制添加到视口的组件是以其首选大小还是以视口宽度显示。 JLayer委托给JPanel,但由于JPanel没有实现Scrollable接口,JLayer实现将返回&#34; false&#34;这意味着组件应以其首选宽度显示。

所以解决这个问题的方法是使用JLayer的包装面板:

//scroll = new JScrollPane(component);
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add( component );
scroll = new JScrollPane(wrapper);

现在,添加到滚动窗格的视口的组件在两种情况下都是JPanel,所以它们的行为方式应该相同。