0

我已经在我的 JFrame 中覆盖了 validate() 以便我可以手动控制几个嵌套的 JPanel 的大小(它们用于将内容滚动到屏幕上,并且我所知道的布局管理器没有让您在边界之外布置组件父容器)。这在拖动窗口以调整其大小时可以正常工作,但是当单击“最大化”按钮时,将调用 validate(),调用 setPreferredSize(),但面板大小不会更新。在 XP 上看到的问题,在 OSX 中没有看到。

public void validate() {
    super.validate();

    LOGGER.debug("Validate called on Frame. Resizing panel");
    if (inited == true) {
        Dimension size = panelLeft.getSize();
        int referenceHeight = size.height;
        LOGGER.info("referenceHeight is " + referenceHeight);
        size = lower.getSize();
        size.height = referenceHeight;
        lower.setPreferredSize(size);
        lower.setMinimumSize(size);
        size.height = size.height * 2;
        movingPanel.setPreferredSize(size);
        movingPanel.setMinimumSize(size);
        LOGGER.info("sizes now: panel: " + lower.getSize().height + ", scrollpane: "
                + movingPanel.getSize().height);
        if (panelSlideController != null) {
            panelSlideController.redraw();
            LOGGER.debug("redrawing panel slide controller");
        }
    }
}

panelLeft 是一个面板,由其布局管理器自动调整大小为框架的全高。所以它的高度被用作参考。

相关面板布置:

 ----------------------------------
|scrollPane                        |
| -------------------------------- |
||movingPanel                     ||
|| ------------------------------ ||
|||upper                         |||
|||                              |||
|||                              |||
|||                              |||
|| ------------------------------ ||
|| ------------------------------ ||
|||lower                         |||
|||                              |||
|||                              |||
|||                              |||
|| ------------------------------ ||
| -------------------------------- |
 ----------------------------------

用户只能看到此布局的上半部分。MovingPanel JPanel 位于 JScrollPane 的视口中。目标是保持上面板占据所有可见的垂直空间,因此与封闭的 JFrame 高度大致相同。下面板已准备好滚动并保持与上面板相同的高度。通过保持lower.height == panelLeft.height 和movingPanel.height == panelLeft.heightx2,这意味着movingPanel 的一半正在显示,而上端位于Frame 的底部。

就像我说的,工作正常。拖动窗口时,一些示例输出为:

2013-06-20 23:15:41,298 [WT-EventQueue-0] DEBUG s.billing.ui.Form  - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO  s.billing.ui.Form  - newheight is 617
2013-06-20 23:15:41,298 [WT-EventQueue-0] INFO  s.billing.ui.Form  - sizes now: panel: 607, scrollpane: 1214
2013-06-20 23:15:41,538 [WT-EventQueue-0] DEBUG s.billing.ui.Form  - Validate called on Frame. Resizing panel
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO  s.billing.ui.Form  - newheight is 640
2013-06-20 23:15:41,538 [WT-EventQueue-0] INFO  s.billing.ui.Form  - sizes now: panel: 636, scrollpane: 1272

最大化窗口时,输出如下:

2013-06-20 22:08:21,234 [WT-EventQueue-0] DEBUG s.billing.ui.Form  - Validate called on Frame. Resizing panel
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO  s.billing.ui.Form  - newheight is 783
2013-06-20 22:08:21,234 [WT-EventQueue-0] INFO  s.billing.ui.Form  - sizes now: panel: 543, scrollpane: 1086

我在那里添加了 setMinimumSize 以试图更具说服力,但它没有帮助。

任何想法都非常欢迎

编辑:添加了 SSCCE

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
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.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.UIManager;

class Frame extends JFrame {

    private JPanel panelLeft;
    private JScrollPane scrollPane;
    private JPanel movingPanel;
    private JPanel upper;
    private JPanel lower;
    private boolean inited;
    private JLabel labelUpper;
    private JLabel labelLower;
    private JButton scrollBtn;
    private Frame.PanelSlideController panelSlideController;
    private JButton resizeBtn;

    private boolean lowerShowing = false;

    public Frame() {
        getContentPane().setLayout(new BorderLayout());

        panelLeft = new JPanel();
        panelLeft.setBackground(Color.CYAN);
        panelLeft.setPreferredSize(new Dimension(300, 400));
        getContentPane().add(panelLeft, BorderLayout.WEST);

        labelUpper = new JLabel("upper");
        panelLeft.add(labelUpper);

        labelLower = new JLabel("lower");
        panelLeft.add(labelLower);

        resizeBtn = new JButton("resize");
        resizeBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doResize();
            }
        });
        panelLeft.add(resizeBtn);

        scrollBtn = new JButton("Scroll");
        scrollBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doScroll();
            }
        });
        panelLeft.add(scrollBtn);

        scrollPane = new JScrollPane();
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

        movingPanel = new JPanel(new GridLayout(2, 1));
        movingPanel.setOpaque(false);
        movingPanel.setPreferredSize(new Dimension(300, 400));

        upper = new JPanel();
        upper.setBackground(Color.YELLOW);
        movingPanel.add(upper);

        lower = new JPanel();
        lower.setBackground(Color.RED);
        movingPanel.add(lower);

        scrollPane.setViewportView(movingPanel);

        getContentPane().add(scrollPane, BorderLayout.EAST);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();

        inited = true;
    }

    /**
     * This is a manual step instead of overriding validate()
     */
    protected void doResize() {
        // Get the height we want
        int referenceHeight = panelLeft.getSize().height;

        // Update the height of the lower panel to equal this
        Dimension size = lower.getSize();
        size.height = referenceHeight;
        lower.setPreferredSize(size);
        lower.setMinimumSize(size);

        // Update the height of the surrounding panel
        size = scrollPane.getSize();
        size.height = referenceHeight * 2;
        movingPanel.setPreferredSize(size);
        movingPanel.setMinimumSize(size);

        if (panelSlideController != null) {
            panelSlideController.redraw();
            System.out.println("redrawing panel slide controller");
        }

        upper.invalidate();
        lower.invalidate();
        scrollPane.revalidate();
    }

    protected void doScroll() {
        panelSlideController = new PanelSlideController(scrollPane, 20);
        int scrollDirection = lowerShowing ? -1 : 1;
        panelSlideController.scrollY(panelLeft.getHeight() * scrollDirection);
        lowerShowing = !lowerShowing;
    }

    @Override
    public void validate() {
        super.validate();

        System.out.println("Validating");
        if (inited) {
            labelUpper.setText("upper: " + upper.getSize().height);
            labelLower.setText("lower: " + lower.getSize().height);
        }
    }

    class PanelSlideController implements ActionListener {

        private final JScrollPane scrollPane;
        private final int speed;
        private Timer timer;
        private int endPos;

        private boolean scrollingPositive;

        public PanelSlideController(JScrollPane scrollPane, int speed) {
            this.scrollPane = scrollPane;
            this.speed = speed;
        }

        public void scrollY(int scrollDistance) {
            endPos = scrollPane.getViewport().getViewPosition().y + scrollDistance;
            scrollingPositive = scrollDistance > 0;
            timer = new Timer(speed, this);
            timer.start();
        }

        public void redraw() {
            JViewport viewport = scrollPane.getViewport();
            Point position = viewport.getViewPosition();

            if (scrollingPositive) {
                position.y = endPos;
            }
            else {
                position.y = 0;
            }
            viewport.setViewPosition(position);

        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JViewport viewport = scrollPane.getViewport();
            Point position = viewport.getViewPosition();
            int offset = scrollingPositive ? 10 : -10;
            position.y += offset;
            viewport.setViewPosition(position);

            if ((scrollingPositive && position.y >= endPos)
                    || (!scrollingPositive && (position.y <= endPos || position.y <= 0))) {
                timer.stop();
            }
        }
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (Exception e) {
            e.printStackTrace();
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Frame().setVisible(true);
            }
        });
    }
}

对,所以上面是可以运行的。panelLeft(青色)用作参考高度。我将代码从 validate() 中移出,而是通过单击调整大小按钮来运行。

因此,您可以设置红色面板(下部)在单击滚动之前可见,此时黄色向上滚动,而红色滚动进入视图。然后再朝相反的方向返回。为此,我需要黄色面板占据所有垂直高度,如果我可以使用布局管理器做到这一点,那就好。

我已经更新了原始片段和“图表”以反映此处使用的名称。

谢谢

4

1 回答 1

0

好的,感谢使用 LayoutManager 的提示,我朝那个方向做了一些挖掘。原来 JScrollPanes 使用 JViewports,而 JViewports 又使用 LayoutManager 的 ViewportLayout 实现。这些可以控制其委托的“视图”组件。从下面修改后的代码中可以看出,我现在在这里重写了 layoutContainer 方法,以使我的“movingPanel”相对于视口的高度加倍,这明显比以前更好,并且在 XP 中最大化时。

谢谢

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Point;
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.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.ViewportLayout;

class Frame extends JFrame {

    private JPanel panelLeft;
    private JScrollPane scrollPane;
    private JPanel movingPanel;
    private JPanel upper;
    private JPanel lower;
    private boolean inited;
    private JLabel labelUpper;
    private JLabel labelLower;
    private JButton scrollBtn;
    private Frame.PanelSlideController panelSlideController;
    private JButton resizeBtn;

    private boolean lowerShowing = false;

    public Frame() {
        getContentPane().setLayout(new BorderLayout());

        panelLeft = new JPanel();
        panelLeft.setBackground(Color.CYAN);
        panelLeft.setPreferredSize(new Dimension(300, 400));
        getContentPane().add(panelLeft, BorderLayout.WEST);

        labelUpper = new JLabel("upper");
        panelLeft.add(labelUpper);

        labelLower = new JLabel("lower");
        panelLeft.add(labelLower);

        scrollBtn = new JButton("Scroll");
        scrollBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doScroll();
            }
        });
        panelLeft.add(scrollBtn);

        scrollPane = new JScrollPane();
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);

        movingPanel = new JPanel(new GridLayout(2, 1));
        movingPanel.setOpaque(false);
        movingPanel.setPreferredSize(new Dimension(300, 400));

        upper = new JPanel();
        upper.setBackground(Color.YELLOW);
        movingPanel.add(upper);

        lower = new JPanel();
        lower.setBackground(Color.RED);
        movingPanel.add(lower);

        // ------------------------------
        // This is the key bit
        // ------------------------------
        JViewport viewport = new JViewport() {
            @Override
            protected LayoutManager createLayoutManager() {
                return new ViewportLayout() {

                    @Override
                    public void layoutContainer(Container parent) {
                        JViewport vp = (JViewport) parent;
                        Component view = vp.getView();

                        Dimension viewPrefSize = view.getPreferredSize();
                        Dimension vpSize = vp.getSize();
                        Dimension viewSize = new Dimension(viewPrefSize);

                        viewSize.width = vpSize.width;
                        viewSize.height = vpSize.height * 2;

                        vp.setViewSize(viewSize);
                    }
                };
            }

        };
        scrollPane.setViewport(viewport);
        viewport.setView(movingPanel);
        // ------------------------------
        // End of key bit
        // ------------------------------

        getContentPane().add(scrollPane, BorderLayout.EAST);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();

        inited = true;
    }

    protected void doScroll() {
        panelSlideController = new PanelSlideController(scrollPane, 20);
        int scrollDirection = lowerShowing ? -1 : 1;
        panelSlideController.scrollY(panelLeft.getHeight() * scrollDirection);
        lowerShowing = !lowerShowing;
    }

    @Override
    public void validate() {
        super.validate();

        System.out.println("Validating");
        if (inited) {
            labelUpper.setText("upper: " + upper.getSize().height);
            labelLower.setText("lower: " + lower.getSize().height);
        }
    }

    class PanelSlideController implements ActionListener {

        private final JScrollPane scrollPane;
        private final int speed;
        private Timer timer;
        private int endPos;

        private boolean scrollingPositive;

        public PanelSlideController(JScrollPane scrollPane, int speed) {
            this.scrollPane = scrollPane;
            this.speed = speed;
        }

        public void scrollY(int scrollDistance) {
            endPos = scrollPane.getViewport().getViewPosition().y + scrollDistance;
            scrollingPositive = scrollDistance > 0;
            timer = new Timer(speed, this);
            timer.start();
        }

        public void redraw() {
            JViewport viewport = scrollPane.getViewport();
            Point position = viewport.getViewPosition();

            if (scrollingPositive) {
                position.y = endPos;
            }
            else {
                position.y = 0;
            }
            viewport.setViewPosition(position);

        }

        @Override
        public void actionPerformed(ActionEvent e) {
            JViewport viewport = scrollPane.getViewport();
            Point position = viewport.getViewPosition();
            int offset = scrollingPositive ? 10 : -10;
            position.y += offset;
            viewport.setViewPosition(position);

            if ((scrollingPositive && position.y >= endPos)
                    || (!scrollingPositive && (position.y <= endPos || position.y <= 0))) {
                timer.stop();
            }
        }
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (Exception e) {
            e.printStackTrace();
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Frame().setVisible(true);
            }
        });
    }
}

注意:我在删除它之前打开了一个后续问题,并在此处包含标题以用于 SEO 目的:如何使用 Swing 布局管理器来包含框架外的对象?

于 2013-06-26T04:15:45.647 回答