我一直在设计一个基于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。
如何解决这个问题的任何想法都将不胜感激。
编辑:
我对此错误的所有研究都表明它与组件的Z轴位置有关。我尝试更改添加调用以指定z位置
super.add(window, getComponentCount());
但没有变化。仍然得到相同的IllegalArgumentException。
答案 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();
}
}