【问题标题】:switching jpanels as a jframe contentpane while running运行时将 jpanels 切换为 jframe 内容窗格
【发布时间】:2020-11-09 20:12:37
【问题描述】:

我的程序创建一个 jframe > 将内容窗格设置为 jpanel > 一次或多次使用 repaint() > 将内容窗格设置为另一个 jpanel > 再次使用一些 repaint() > 等等... (注意:出于某种原因,我必须先创建 jframe,然后再添加 jpanel)

但仅调用 setContentPane(newjpanel) 之后我将无法使用 repaint(),而我知道的唯一可以解锁的方法是手动调整窗口大小

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MyJFrame extends JFrame {
    private static final long serialVersionUID = 1L;

    public MyJFrame() {
        // default panel for visual feedback
        JPanel pan = new JPanel();
        pan.setBackground(Color.green);
        this.setContentPane(pan);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TODO
        this.setSize(400, 400);
        // other unrelated configuration stuff
        // ...

        this.setVisible(true);
    }

    public void switchPanel(JPanel pan) {
        this.setContentPane(pan);
        // pan.setVisible(true); doesnt work
        // pan.revalidate(); neither
    }

    public static void main(String[] args) {

        MyJFrame frame = new MyJFrame();

        //...
        JPanel mypan = new MyJPanel();
        // there i have an IO operation to get an image, without this delay this code work just fine
        // so i put this thread.sleep to simulate this delay
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
        }

        frame.switchPanel(mypan);

        while (true) {
            // this repaint wont work unless i change the window size
            mypan.repaint();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
        }
    }

    public static class MyJPanel extends JPanel {
        private static final long serialVersionUID = 1L;
        public Point pos = new Point(0, 0);

        public synchronized void paintComponent(Graphics g) {
            super.paintComponent(g);
            // i made the square move for visual feedback
            pos.x += 1;
            g.fillRect(pos.x, pos.y, 10, 10);
        }
    }

}

这段代码应该显示一个正向 +x 方向的正方形

在调整窗口大小之前它不会发生

我知道的另一个选项是将 jframe 的可见性设置为 false 更改窗格然后将其设置为 true 但它在视觉上确实令人不快

所以我的问题是,我怎样才能在运行时添加/删除 jpanels? (在 switchPanel 中添加代码,添加容器,真的什么)

(第二个注意事项:如果可能的话,我想在运行时真正添加/删除 jpanels,我知道使用例如卡片布局并关闭和打开面板可见性,但在那里不能这样做)

【问题讨论】:

  • 通常在 Swing 中,您在开始时创建所有 JPanel,然后使用 CardLayout 或 TabbedPane 显示或隐藏特定的 JPanel。如果您不想正确使用 Swing,那么您只能靠自己了。
  • 我知道使用例如卡片布局 - 这就是你应该使用的。此外,您不应该使用 Thread.sleep()。对于动画,您应该使用 Swing Timer。
  • @GilbertLeBlanc 看,这有点粗鲁,不是因为我远离我不能问的约定,你可能不喜欢我上一个问题,但我真的尽力了.问题是,我有一个项目要做,我可能没有选择正确的方法来实现它,但现在我做到了,在某些时候我很想得到帮助,但我不会强迫任何人。感谢您的理解
  • @camickr 谢谢,我知道 Thread.sleep,这主要是测试代码,让我的渲染引擎在继续之前工作。关于卡片布局,我知道,但是这样做需要重构很多,而不会从中获得很多好处,如果我必须在另一个项目中以正确的方式使用 swing,但现在,它会有所帮助我更多地找到我所问的答案'^^,感谢您的理解
  • 这将帮助我更多地找到我所要求的确切答案 我们需要更好地了解您的要求。创建一个面板然后在类的构造函数中立即替换它是没有意义的。为每个问题发布适当的minimal reproducible example。因此,“MRE”应该有一个带有面板的框架和一个您按下以替换内容窗格的 JButton。这将更好地代表您的应用程序。 while true 逻辑什么都不做,也不是“MRE”所必需的

标签: java swing


【解决方案1】:

创建组件时,它们的大小为 0,因此没有可绘制的内容。

当您将组件动态添加到可见框架时,您需要 revalidate() 将组件添加到的父容器。这将调用父容器上的布局管理器,并且子组件将被赋予一个大小/位置。

通常这意味着您 revalidate() 和您添加组件的 JPanel。但是,在这种情况下,由于您要替换内容窗格,因此最简单的方法是 revalidate() 框架:

public void switchPanel(JPanel pan) {
    this.setContentPane(pan);
    revalidate();
}

【讨论】:

  • 确实解决了mre的问题。 (我的主程序仍然无法工作,但我想此时我的代码中的一个非常愚蠢的错误或某种黑魔法有什么不同:/)无论如何感谢您的回答
  • 是的,这是第一步——了解基本问题。 MRE 的重点是简化问题以隔离原因。然后,正如您所说,这是查看实际应用程序和 MRE 之间有什么不同的问题。我建议问题是 Thread.sleep() 正在导致问题的事件调度线程 (EDT) 上执行。这就是为什么我说你应该使用 Swing Timer。在此 MRE 中,代码不在 EDT 上执行。在您的真实示例中,它可能由于单击按钮而执行,在这种情况下,代码确实在 EDT 上运行。
  • 好吧,在我的代码中,它实际上在与 mre 相同的位置执行,但 jpanel 实例化和对 switchpanel 的调用是在另一个对象的构造函数中进行的。是否有可能在没有显式调用任何与线程相关的函数的情况下更改线程? (或thread.sleep)
  • 我真的不明白你在做什么,所以我不能提供进一步的建议。如果您在没有用户交互的情况下交换面板,那么您的代码是完全错误的。没有理由设置默认内容面板然后交换它。进行交换的唯一原因是框架在原始内容窗格中可见,然后用户启动了面板的交换。您需要阅读 Concurrency 上的 Swing 教程。所有 Swing 组件都应在 EDT 上创建。在 EDT 上使用 Thread.sleep() 是错误的。
  • 我的意思是,我有理由这样做,只是需要很长时间来解释和辩论。关于thread.sleep,我只是将它用于调试(我认为到目前为止EDT中没有运行任何代码?)。无论如何感谢您的链接
【解决方案2】:

这是我发现的一个脏修复(适用于 mre 和我的主代码)

public void switchPanel(JPanel pan) {
        this.setContentPane(pan);
        Dimension dim = this.getSize();
        int y = dim.height;
        int x = dim.width;
        this.setSize(x+1,y);
        this.setSize(x, y);
    }

这似乎相当于关闭你的电脑来关闭一个窗口,所以不是很合适。

【讨论】:

  • 到目前为止,我认为 EDT 中没有运行任何代码? - 这是一个问题。 应在 EDT 上创建和更新所有 Swing 组件。 如果您不解决底层设计问题,那么您在开发应用程序时会遇到更多麻烦。不应使用这样的 hack。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
  • 2019-03-11
  • 1970-01-01
  • 2011-11-10
相关资源
最近更新 更多