1

我一直在设计一个基于 Swing 的桌面 RPG 程序,以促进带有 GUI 控制元素的基于文本的角色扮演。

为了促进这一点,每个正在运行的客户端都会获得一个包含所有重要 JFrame 的主桌面(托管客户端上的“GM 桌面”和远程客户端上的“播放器桌面”)。此外,GM 和玩家都可以为角色打开“Perspective Desktops”,为他们提供一个单独的 JDesktopPane,其中包含提供角色视角的“Role Play Chat Window”,以及额外的 JInternalFrames,例如“Character Sheet Window”等.

用户使用 JTabbedPane 在桌面之间导航。

我遇到的问题是我希望能够在桌面之间移动的一些窗口。例如,如果 OOC(字符外)聊天在用户处于透视桌面时收到一条消息,我希望 OOC 聊天窗口有一个选项可以自动重新定位到当前桌面,以便用户看到立即留言。同样,我希望播放器能够使用菜单栏将某些窗口“调用”到当前桌面。

但是,当我尝试将 JInternalFrame 从一个 JDesktopPane 移动到另一个时,我收到一个异常。

com.finnickslab.textroleplayonline.exceptions.CommandEventHandlingException
An exception was thrown during command handling. CommandEvent type: UI_OOC_CHAT (26).
Cause Exception: java.lang.IllegalArgumentException
illegal component position
java.awt.Container.addImpl(Unknown Source)
javax.swing.JLayeredPane.addImpl(Unknown Source)
javax.swing.JDesktopPane.addImpl(Unknown Source)
java.awt.Container.add(Unknown Source)
com.finnickslab.textroleplayonline.ui.GameDesktop.receiveTransfer(GameDesktop.java:80)
com.finnickslab.textroleplayonline.ui.GameDesktop.access$0(GameDesktop.java:74)
com.finnickslab.textroleplayonline.ui.GameDesktop$2.run(GameDesktop.java:69)
com.finnickslab.textroleplayonline.ui.UI.invokeEvent(UI.java:818)
com.finnickslab.textroleplayonline.ui.GameDesktop.transfer(GameDesktop.java:62)
com.finnickslab.textroleplayonline.ui.UI$HostCommandHandler.handle(UI.java:605)
com.finnickslab.textroleplayonline.comm.Server$3.run(Server.java:324)

我程序中的所有 JInternalFrames 都来自 JInternalFrame 的同一个子类(“InternalWindow”)。

该异常使它看起来有点复杂,但归结为调用 JDesktopPane.remove(JInternalFrame) 然后调用 JDesktopPane.add(JInternalFrame)。

然后,只要在 GameDesktop 第 80 行调用“add”方法,我就会收到该异常。

/**
 * Transfers the specified InternalWindow from this GameDesktop to
 * the specified GameDesktop. Use this method to prevent
 * automatic removal of listeners performed with the
 * {@link GameDesktop.remove(InternalWindow)} method.
 */
public synchronized void transfer(
      final InternalWindow window,
      final GameDesktop gd) {
   final GameDesktop desktop = this;
   contents.remove(window);

   UI.invokeEvent(new Runnable() {
      @Override
      public void run() {
         desktop.remove((JInternalFrame) window);
         desktop.validate();
         desktop.repaint();

         gd.receiveTransfer(window);
      }
   });
}

private synchronized void receiveTransfer(InternalWindow window) {
   contents.add(window);

   window.changeDesktop(this);
   window.center();
   this.add((JInternalFrame) window);    // LINE 80
   this.validate();
   this.repaint();
   window.resetPosition();
}

“UI.invokeEvent(Runnable)”方法是我为 SwingUtilities.invokeAndWait(Runnable) 编写的一种便捷方法。它检查当前线程是否是 EDT,如果是,则立即执行 run() 方法。否则,它使用 invokeAndWait(Runnable) 在 EDT 上调度 runnable。

任何有关如何解决此问题的想法将不胜感激。

示例 1 示例 2

编辑:

我对这个错误的所有研究表明它与组件的 Z 轴位置有关。我尝试更改 add 调用以指定 z 位置

super.add(window, getComponentCount());

但没有变化。仍然得到相同的 IllegalArgumentException。

4

1 回答 1

1

看看运行时是否遇到相同的错误。如果不是,则问题不在于切换内部框架的父级,而在于同步。

public class IFSwitch extends JDesktopPane {

    final JDesktopPane pane1 = this;

    public IFSwitch() {

        JFrame frame1 = new JFrame("Frame1");
        JFrame frame2 = new JFrame("Frame2");
//      JDesktopPane pane1 = new JDesktopPane();
        JDesktopPane pane2 = new JDesktopPane();
        final JInternalFrame if1 = new JInternalFrame();

        frame1.add(pane1);
        frame2.add(pane2);
        pane1.add(if1);

        if1.setBounds(10, 10, 100, 100);
        frame1.setBounds(100, 100, 200, 200);
        frame2.setBounds(500, 500, 200, 200);
        frame1.setVisible(true);
        frame2.setVisible(true);
        if1.setVisible(true);

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        pane2.add(if1);
        pane1.remove(if1); // You don't even need this line.
        pane1.repaint();

    }

    public static void main(String[] args) {

        new IFSwitch();
    }
}
于 2014-05-10T10:57:59.863 回答