【发布时间】:2018-12-21 22:39:00
【问题描述】:
好的,我的 Swing 事件侦听器有问题...简短介绍我开发了一个 Java 应用程序,其 Swing UI 由 MVC 模式结构化。
- MyView -> 文本由用户更改,视图应通过控制器通知模型
- MyModel -> 存储数据并通过控制器通知视图有关更改
- MyController -> 用于通知模型和视图更改的接口
基于此类的模型和视图仅通过控制器类连接。视图类包含一个用于用户输入的文本字段,它应该使用用户输入更新模型类而不按下按钮。这意味着我需要一个用于等待用户输入/更改文本的 JTextField 侦听器...
我试过DocumentListener但是不行,抛出异常:java.lang.IllegalStateException: Attempt to mutate in notification
我认为这里的问题是,如果属性更改并且控制器再次通知/更改视图,模型类也会调用控制器 -> 结果:无限循环
我发现的两种解决方案都不适合我:
Swing JTextField on text change
JTextField listener when text changes that modifies textField's text
MyModel.java
public void setHost(String host) // Method called by controller to change model
{
String oldHost = this.host;
this.host = host;
this.firePropertyChange("Host", oldHost, this.host); // Model inform view about changes
}
MyView.java
@Override public void modelPropertyChange(final PropertyChangeEvent event)
{
// Method used to update view and called by controller
if(event.getPropertyName().equals("Username"))
{
String username = (String) event.getNewValue();
this.nameField.setText(username);
}
}
问题是当调用文档侦听器时,因为用户输入了模型更改的内容调用视图的属性更改方法,并且视图将文本替换为相同的文本,这再次引发文档更改事件并调用侦听器。 .. 无限循环
我尝试使用ActionListener,它工作正常,但用户有必要按回车键分配更改... 是否有任何其他选项可以在没有 @ 的情况下侦听 JTextField 中的文本更改987654329@?或者我应该通过我的 MVC 模式改变什么来解决这个问题?
编辑
我尝试了 Peter Walser 的解决方案,但抛出了一个新异常:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.x1c1b.carrierpigeon.service.mvc.AbstractController.setModelProperty(AbstractController.java:62)
at org.x1c1b.carrierpigeon.desktop.ui.controller.LoginController.changeUsername(LoginController.java:12)
at org.x1c1b.carrierpigeon.desktop.ui.view.LoginView$UsernameChangedListener.updateFieldState(LoginView.java:221)
at org.x1c1b.carrierpigeon.desktop.ui.view.LoginView$UsernameChangedListener.insertUpdate(LoginView.java:203)
at javax.swing.text.AbstractDocument.fireInsertUpdate(AbstractDocument.java:201)
at javax.swing.text.AbstractDocument.handleInsertString(AbstractDocument.java:748)
at javax.swing.text.AbstractDocument.insertString(AbstractDocument.java:707)
at javax.swing.text.PlainDocument.insertString(PlainDocument.java:130)
at org.x1c1b.carrierpigeon.desktop.ui.util.TextFieldLimit.insertString(TextFieldLimit.java:26)
at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:669)
at javax.swing.text.JTextComponent.replaceSelection(JTextComponent.java:1328)
at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(DefaultEditorKit.java:884)
at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1668)
at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)
at javax.swing.JComponent.processKeyBindings(JComponent.java:2929)
at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)
at java.awt.Component.processEvent(Component.java:6316)
at java.awt.Container.processEvent(Container.java:2239)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:835)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1103)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:974)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:800)
at java.awt.Component.dispatchEventImpl(Component.java:4760)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
at java.awt.EventQueue$4.run(EventQueue.java:733)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Caused by: java.lang.IllegalStateException: Attempt to mutate in notification
at javax.swing.text.AbstractDocument.writeLock(AbstractDocument.java:1338)
at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:658)
at javax.swing.text.JTextComponent.setText(JTextComponent.java:1669)
at org.x1c1b.carrierpigeon.desktop.ui.view.LoginView.modelPropertyChange(LoginView.java:76)
at org.x1c1b.carrierpigeon.service.mvc.AbstractController.propertyChange(AbstractController.java:47)
at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
at org.x1c1b.carrierpigeon.service.mvc.AbstractModel.firePropertyChange(AbstractModel.java:27)
at org.x1c1b.carrierpigeon.desktop.ui.model.LoginModel.setUsername(LoginModel.java:39)
... 52 more
JTextField的文档在通知模型的过程中似乎仍然被锁定,因为它调用了setText方法抛出了异常,这个操作是非法的,但我不知道为什么?!
编辑
目前我通过Peter Walser的指令和第一个解决方案结合执行EDT上DocumentListener设置的指令解决了这个错误!
【问题讨论】:
-
@camickr 是的,但我没有尝试过滤文本...这并不是要在用户键入时更改文本字段中的文本...模型通知/每次更改自身时都更改
JTextField中的文本,因为有第二个控制器通过套接字的数据更改模型...所以模型必须可以更改 JTextField 文本,但在这种特殊情况下当用户而不是套接字更改 JTextField 文本时,模型并不打算再次更改视图... -
I tried the solution of Peter Walser- Peter 提出了两种解决方案。不知道你试过哪一种?我会使用第一种方法。but I got a new exception:...- 已经给出了如何避免该问题的解决方案。发布一个正确的minimal reproducible example 来证明问题,这样我们就不必猜测你在做什么。这就是您所需要的一个带有文本字段和按钮的 JFrame。用户可以在文本字段中输入文本来更新模型。该按钮将直接向模型添加文本。那么也许我们可以帮助调试。 -
@camickr 也感谢您抽出宝贵的时间,我用第一个解决方案解决了这个问题
标签: java swing model-view-controller jtextfield documentlistener