SWT MenuItem监听器无响应 报Widget is disposed错误
问题根因
你遇到的点击无响应、关闭抛出Widget is disposed错误,完全是SWT生命周期逻辑写错了,一共三个核心问题:
- 启动顺序完全颠倒。
AppContext里你先调用了monitor.refresh(),这个方法内部是SWT的全局事件循环,会一直阻塞直到主窗口被关闭,后面绑定监听器的ff4jController.initController()在窗口运行阶段根本不会执行——等你关了主窗口,这行代码才跑,这时候所有UI组件都已经销毁了,点击菜单当然没反应。 - 重复编写事件循环、提前销毁全局Display。SWT应用全局只需要一个事件循环,你在主窗口
refresh()方法里已经写了事件循环逻辑,LoginForm里又写了一套readAndDispatch循环,甚至关闭LoginForm时直接调用monitor.getDisplay().dispose()把全局UI资源销毁了,主窗口的所有组件都会变成已销毁状态,关程序自然抛错。 LoginForm的成员变量赋值缺失。你定义了静态成员static Monitor monitor,但构造函数传入的参数m从来没赋值给这个静态变量,代码运行到new Shell(monitor.getDisplay())时会直接触发空指针。
修复步骤
按下面改完就能正常运行:
- 调整AppContext的执行顺序,先绑定监听器再启动事件循环
public class AppContext { public static void main(final String[] args) { final FF4jContext ff4jContext = new FF4jContext(); final Monitor monitor = new Monitor(); // 先初始化控制器、绑定菜单事件 final FF4JContextController ff4jController = new FF4JContextController(monitor, ff4jContext); ff4jController.initController(); // 所有初始化完成后再启动事件循环 monitor.refresh(); } }
- 删掉LoginForm里多余的事件循环和Display销毁逻辑,LoginForm只需要负责自身UI的创建和打开
public class LoginForm { private Shell shellLoginForm; public LoginForm(final Monitor m) { // 传入主窗口Shell作为父级,自动复用同一个Display,加APPLICATION_MODAL样式可以实现模态弹窗效果 shellLoginForm = new Shell(m.getShellMain(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); shellLoginForm.setText("登录"); shellLoginForm.setSize(400, 300); // 这里补充登录框的输入框、按钮等组件初始化逻辑 shellLoginForm.open(); // 不要在这里写while事件循环!不要调用display.dispose()! } }
- 给Monitor类补充主Shell的getter方法,供LoginForm调用
// 在Monitor类里加这个方法 public Shell getShellMain() { return shellMain; }
- 全局只保留Monitor的
refresh()方法里的那一套事件循环和最终Display销毁逻辑,其他地方不要手动调用display.dispose()。
注意:SWT的UI事件循环是全局单例的,所有窗口、弹窗共用同一个循环,不需要给每个弹窗单独写
readAndDispatch循环。如果需要弹窗打开时禁止操作主窗口,给弹窗Shell加SWT.APPLICATION_MODAL样式即可,自己写循环阻塞线程反而会导致事件分发混乱、资源提前释放。
内容的提问来源于stack exchange,提问作者younes alaoui
相关产品推荐
相关产品推荐

