简体   繁体   English

Java, jframe 在拖动时调整大小问题

[英]Java, jframe resizing while dragging issue

I would like my jframe to change its size and contents depending on the x coordinate.我希望我的 jframe 根据 x 坐标更改其大小和内容。 Initially the jframe appears in the 'primary zone' (x <= 1400) witn a panel sized 500x500 added to the jframe's content pane.最初 jframe 出现在“主要区域”(x <= 1400)中,并在 jframe 的内容窗格中添加了一个大小为 500x500 的面板。
Desired : When it is dragged and leaves the 'primary zone' and enters the 'secondary zone' everything is removed from the content pane, the panel gets wrapped into a jscrollpane sized 200x200, and the jscrollpane is added the content pane. Desired :当它被拖动并离开“主要区域”并进入“次要区域”时,所有内容都将从内容窗格中删除,面板被包裹到大小为 200x200 的 jscrollpane 中,并将 jscrollpane 添加到内容窗格中。 When the jframe leaves the 'secondary zone' the jscrollpane is removed from the content pane and the panel is added back.当 jframe 离开“次要区域”时,jscrollpane 将从内容窗格中删除并重新添加面板。
Actual : Results are not stable.实际:结果不稳定。 When the jframe leaves the primary zone I can see some flipping.当 jframe 离开主要区域时,我可以看到一些翻转。 Scrollbars appear but and the frame changes its size but then immediately resized back to the previous size.滚动条出现,但框架改变了它的大小,但随后立即调整回原来的大小。 Stopping at breakpoints inside runnables in the changeSizeAndContent invokeLater codeblocks (not a good practice actually) brings the desired result and so does a conditional breakpoint.在 changeSizeAndContent invokeLater 代码块中的可运行文件内的断点处停止(实际上不是一个好的做法)会带来所需的结果,条件断点也是如此。
There is some Swing multithreading taking place which I do not understand.发生了一些我不明白的 Swing 多线程。 I can see the EDT calling EventQueue's dispatchEvent and the COMPONENT_RESIZED (new, correct size) events triggered by runnables in the changeSizeAndContent are followed by COMPONENT_MOVED (old, now incorrect size) events which reference the component with its old size.我可以看到 EDT 调用 EventQueue 的 dispatchEvent 并且由 changeSizeAndContent 中的可运行对象触发的 COMPONENT_RESIZED(新的,正确的大小)事件之后是 COMPONENT_MOVED(旧的,现在不正确的大小)事件,它们引用了具有旧大小的组件。
Tried with Java 8 and 11.尝试使用 Java 8 和 11。

package Jna;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

/* 0------------------1200-1400--------->
 *                      +---------------+
 *                      |   SECONDARY   |
 * +--------------------+---+           |                     
 * |    PRIMARY         | B | x: 1200-  |
 * |    x: 0-1400       | U |           |
 * |                    | F |           |
 * |                    | F |           |
 * |                    | E |           |
 * |                    | R |           |
 * |                    +---+-----------+
 * |                        |
 * +------------------------+
 */
public class FrameDemo3 {

    static final JPanel panel;
    static final JScrollPane jsp;
    
    static {
        panel = getInitializedPanel();
        jsp = getInitilalizedJScrollPane();
    }
    
    static boolean isPrimaryZone = true;
    static boolean isCurrentPrimaryZone = true;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGUI());
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("FrameDemo");

        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent e) {
                int x = frame.getX();

                if (x > 1400) {
                    isCurrentPrimaryZone = false;
                } else if (x < 1200) {
                    isCurrentPrimaryZone = true;
                }

                if (isPrimaryZone != isCurrentPrimaryZone) {
                    isPrimaryZone = isCurrentPrimaryZone;
                    changeSizeAndContent(frame);
                }
            }
        });
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }

    public static void changeSizeAndContent(JFrame frame) {
        if (isPrimaryZone) {
            SwingUtilities.invokeLater(() -> {
                frame.getContentPane().removeAll();
                frame.getContentPane().add(panel);
                frame.pack();
            });
        } else {
            SwingUtilities.invokeLater(() -> {
                frame.getContentPane().removeAll();
                frame.getContentPane().add(jsp);
                frame.pack();
            });            
        }
    }

    private static JPanel getInitializedPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBackground(Color.WHITE);

        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                panel.add(new JLabel(getLabelText(i, j)), getConstraints(i, j));
            }
        }
        panel.setPreferredSize(new Dimension(500, 500));
        return panel;
    }
    
    private static JScrollPane getInitilalizedJScrollPane() {
        JScrollPane jsp = new JScrollPane(panel,
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        jsp.setPreferredSize(new Dimension(200, 200));
        return jsp;
    }

    static GridBagConstraints getConstraints(int gridX, int gridY) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = gridX;
        gbc.gridx = gridY;
        gbc.ipady = 40;
        gbc.ipadx = 40;
        return gbc;
    }

    static String getLabelText(int gridX, int gridY) {
        StringBuilder sb = new StringBuilder("EXAMPLE ")
                .append(gridX)
                .append(',')
                .append(gridY);
        return sb.toString();
    }

}

As mentioned in the comments, a component can only have a single parent, so an easier solution might be to add the component to the scroll pane and change the scrollbar policy as required.正如评论中提到的,组件只能有一个父级,因此更简单的解决方案可能是将组件添加到滚动窗格并根据需要更改滚动条策略。 The code below demonstrates this.下面的代码演示了这一点。

There is some Swing multithreading taking place which I do not understand?发生了一些我不明白的 Swing 多线程?

The issues appears to be the title bar of the frame.问题似乎是框架的标题栏。

(Note I changed the resizing behaviour to work at 1000/800 instead of 1400, 1200) (请注意,我将调整大小的行为改为在 1000/800 而不是 1400、1200 下工作)

When the frame is moved to location 1001 your logic is invoked and the frame is resized as expected.当框架移动到位置 1001 时,将调用您的逻辑并按预期调整框架的大小。

However, the issue is that additional events on the frame will always be generated.但是,问题是始终会生成帧上的其他事件。

For example if you move the frame to 1001 and hold the mouse down the frame will remain at the correct size.例如,如果您将框架移动到 1001 并按住鼠标,则框架将保持正确的大小。 However as soon as you release the mouse it goes back to the previous size.但是,一旦您松开鼠标,它就会恢复到以前的大小。

Or if you move the frame to location 1002 or greater it goes back to the previous size.或者,如果您将框架移动到位置 1002 或更大,它会恢复到以前的大小。

In both of the above cases code in your application is not executed to change the frame size.在上述两种情况下,您的应用程序中的代码都不会执行以更改帧大小。

I have no idea how to fix this as this logic is controlled by the OS frame widget.我不知道如何解决这个问题,因为这个逻辑是由操作系统框架小部件控制的。 This may explain why it works for user153... I use Windows 10 and I see the problems you describe.这可以解释为什么它适用于 user153 ......我使用 Windows 10,我看到了你描述的问题。

In the code below I added a Swing Timer to reset the frame size to the expected size after a delay of 2 seconds.在下面的代码中,我添加了一个 Swing 计时器,以在延迟 2 秒后将帧大小重置为预期大小。 It is not a practical solution, but it does demonstrate that your code is working as expected, you just can't control the external behaviour of the frame.这不是一个实用的解决方案,但它确实表明您的代码按预期工作,您只是无法控制框架的外部行为。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.*;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class FrameDemo4 {

    static final JPanel panel;
    static final JScrollPane jsp;
    static Dimension preferredSize;

    static {
        panel = getInitializedPanel();
        jsp = getInitilalizedJScrollPane();
    }

    static boolean isPrimaryZone = true;
    static boolean isCurrentPrimaryZone = true;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGUI());
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("FrameDemo");

        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent e) {
                int x = frame.getX();
                System.out.println(x);

                if (x > 1000) {
                    isCurrentPrimaryZone = false;
                } else if (x < 800) {
                    isCurrentPrimaryZone = true;
                }

                if (isPrimaryZone != isCurrentPrimaryZone) {
                    isPrimaryZone = isCurrentPrimaryZone;
                    changeSizeAndContent(frame);
                }
            }
        });

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//        frame.getContentPane().add(panel);
        frame.getContentPane().add(jsp);
        frame.pack();
        frame.setLocation(1000, 0);
        frame.setVisible(true);
    }

    public static void changeSizeAndContent(JFrame frame) {
        System.out.println(isPrimaryZone);

        if (isPrimaryZone) {
                //frame.getContentPane().removeAll();
                //frame.getContentPane().add(panel);
                jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
                jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
                jsp.setPreferredSize( panel.getPreferredSize() );
                frame.pack();
                preferredSize = frame.getPreferredSize();
        } else {
                //frame.getContentPane().removeAll();
                //frame.getContentPane().add(jsp);
                jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
                jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
                jsp.setPreferredSize( new Dimension(200, 200));
                frame.pack();
                preferredSize = frame.getPreferredSize();
        }

        Timer timer = new Timer(2000, (e) -> frame.setSize( preferredSize.width, preferredSize.height ) );
        timer.start();
    }

    private static JPanel getInitializedPanel() {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBackground(Color.WHITE);

        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                panel.add(new JLabel(getLabelText(i, j)), getConstraints(i, j));
            }
        }
        panel.setPreferredSize(new Dimension(500, 500));
        return panel;
    }

    private static JScrollPane getInitilalizedJScrollPane() {
//        JScrollPane jsp = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
//        jsp.setPreferredSize(new Dimension(200, 200));
        JScrollPane jsp = new JScrollPane(panel);
        jsp.setPreferredSize(panel.getPreferredSize());
        return jsp;
    }

    static GridBagConstraints getConstraints(int gridX, int gridY) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = gridX;
        gbc.gridx = gridY;
        gbc.ipady = 40;
        gbc.ipadx = 40;
        return gbc;
    }

    static String getLabelText(int gridX, int gridY) {
        StringBuilder sb = new StringBuilder("EXAMPLE ")
                .append(gridX)
                .append(',')
                .append(gridY);
        return sb.toString();
    }

}

The only solution I can think of is to:我能想到的唯一解决方案是:

  1. Maybe use the Metal LAF.也许使用金属 LAF。 It uses an undecorated frame with a custom title bar它使用带有自定义标题栏的未装饰框架
  2. Create your own title bar to use on an undecorated frame.创建您自己的标题栏以在未装饰的框架上使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM