将JInternalFrames从一个JDesktopPane移动到另一个JDesktopPane

时间:2014-05-09 22:04:51

标签: java swing jinternalframe jdesktoppane

我一直在设计一个基于Swing的桌面RPG程序,以便使用GUI控件元素来促进基于文本的角色扮演。

为了实现这一点,每个正在运行的客户端都会获得一个主桌面(主机客户端上的“GM桌面”和远程客户端上的“Player Desktop”)以及所有重要的JFrame。此外,GM和玩家都可以为角色打开“透视桌面”,为他们提供单独的JDesktopPane,其中包含“角色扮演聊天窗口”,可以提供该角色的视角,以及其他JInternalFrame,如“角色表窗口”等。

用户使用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)

我程序中的所有JInternalFrame都来自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。

如何解决这个问题的任何想法都将不胜感激。

Example 1 Example 2

编辑:

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

super.add(window, getComponentCount());

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

1 个答案:

答案 0 :(得分: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();
    }
}