0

JFrame で validate() をオーバーライドして、ネストされた 2 つの 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 の半分が表示され、上端がフレームの下部にあることを意味します。

私が言ったように、うまくいきます。ウィンドウをドラッグするときの出力例は次のとおりです。

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

OK、LayoutManager を使用するように促されたおかげで、その方向に掘り下げました。JScrollPanes は、LayoutManager の ViewportLayout 実装を使用する JViewports を使用することが判明しました。これらは、委任された「ビュー」コンポーネントを制御します。以下の変更されたコードに見られるように、ここで layoutContainer メソッドをオーバーライドして、ビューポートに関連して「movingPanel」の高さを 2 倍にしました。これは、以前よりも、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 の目的でここにタイトルを含めています: How to use Swing Layout Managers to contain objects out of frame?

于 2013-06-26T04:15:45.647 に答える