Swing 的 JList MVC 实现有问题吗?

发布于 2024-10-12 14:14:00 字数 657 浏览 5 评论 0原文

前段时间我问这个问题。所有解决方案都是解决方法。

现在这不可能了。我感觉这里有些不对劲,但又分不清到底是Swing的MVC模型概念上错了,还是我的想法概念上错了。

问题又来了。我正在使用 JList 来实现文档页面的缩略图列表。如果用户从列表中选择另一个缩略图,则加载该页面。为此,我向 JList 添加了一个 ListSelectionListener,当选择更改时,它会加载该页面。但用户也可以使用另一个控件更改页面。当然,我希望通过在此处选择该页面来将其反映在缩略图列表中。因此,我使用 setSelectedIndex() 来更新 JList。不幸的是,这会产生引发 ListSelectionEvent 的不良影响,导致侦听器重新加载页面。

现在这里出了什么问题?我刚刚从其他地方更改了模型,所以我自然希望视图自行更新,但我不希望它触发事件。 Swing 没有实现 MVC 吗?或者我在这里遗漏了一点?

Some time ago I asked this question. All solutions are workarounds.

Now this can't be. I feel that something is wrong here, but I can't tell if it is Swing's MVC model that is conceptually wrong, or if it is my thinking that is conceptually wrong.

Here is the problem again. I am using a JList to implement a list of thumbnails for the pages of a document. If the user selects another thumbnail from the list, that page is loaded. To do this I added a ListSelectionListener to the JList, which when the selection changes, it loads that page. But the user can also change the page using another control. Naturally, I want this to be reflected in the thumbnail list by having that page selected here. So I setSelectedIndex() to update the JList. Unfortunately this has the unwanted effect of raising a ListSelectionEvent which causes the listener to reload the page.

Now what is wrong here? I just changed the model from somewhere else, so naturally I want the view to update itself, but I don't want it to trigger events. Is Swing not implementing MVC right? Or am I missing a point here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

巷雨优美回忆 2024-10-19 14:14:00

这是我们很多 Swing 程序员必须面对的问题:多个控件修改相同的数据,然后更新反映在每个控件中。在某些时候,某些东西必须对将哪些更新应用于模型有最终的否决权:无论什么东西都需要能够处理多个(可能是冗余的甚至矛盾的)更新并决定如何处理它们。这可能发生在模型层,但理想情况下应该是控制器来执行此操作 - 毕竟,这部分最有可能是业务逻辑所在的位置。

在这方面,Swing 的问题在于 MVC 的控制器部分通常在视图组件和模型之间有所分割,因此很难集中该逻辑。在某种程度上,Action 接口通过将逻辑放在一个位置并允许不同组件共享它来纠正 actionPerformed() 事件的这一问题,但这对其他类型的事件没有帮助,或者当有多个不同类别的事件需要协调时。

那么,答案是遵循 Swing 中暗示但未明确表示的模式:仅在状态实际发生更改时才执行请求的更新,否则不执行任何操作。 JList 本身就是一个这样的例子:如果您尝试将 JList 的选定索引设置为已选定的相同索引,什么也不会发生。不会触发任何事件,也不会发生更新:更新请求实际上被忽略。这是一件好事。这意味着您可以在 JList 上有一个监听器,它将响应新选择的项目,然后依次要求相同的 JList 重新选择相同的项目item 并且您不会陷入病态的递归循环。如果应用程序中的所有模型控制器都这样做,那么在整个地方触发多个重复事件就没有问题 - 每个组件只会在需要时更新自身(并随后触发事件),并且如果确实更新然后它可以触发它想要的所有更新事件,但只有那些尚未收到消息的组件才会对此执行任何操作。

This is an issue that a lot of us Swing programmers have to face: multiple controls modifying the same data, with the update then reflected in each control. At some point, something has to have the ultimate veto on what updates will be applied to the model: whatever that something is needs to be able to handle multiple (potentially redundant or even contradictory) updates and decide what to do with them. This could happen in the model layer, but ideally it should be the controller that does this - this piece, after all, is most likely where the business logic resides.

The problem with Swing, in this regard, is that the controller piece of MVC is often split somewhat between the view component and the model so it can be difficult to have that logic centralised. To some extent the Action interface rectifies this for actionPerformed() events by putting the logic in one place and allowing it to be shared by different components, but this doesn't help for the other types of event, or when there are multiple different classes of event that need to be coordinated.

The answer, then, is to follow a pattern that's hinted at in Swing but not made explicit: only perform the requested update if the state will actually change, otherwise do nothing. An example of this is in the JList itself: if you attempt to set the selected index of a JList to the same index that's already selected nothing will happen. No events will be fired, no updates will occur: the update request is effectively ignored. This is a good thing. This means that you can, for example, have a listener on your JList that will respond to a newly selected item, and then in turn ask the same JList to reselect that same item and you won't get stuck in a pathologically recursive loop. If all the model-controllers in an application do this then there's no problem with multiple, repeated events firing off all over the place - each component will only update itself (and subsequently fire off events) if it needs to, and if it does update then it can fire off all the update events it wants, but only those components that haven't already got the message will do anything about it.

じ违心 2024-10-19 14:14:00

这是预期的行为。

来自 模型-视图-控制器 [维基百科] :

在事件驱动系统中,模型
通知观察者(通常是视图)
当信息发生变化时
他们可以做出反应。

因此,当您在 JList 上调用 setSelectedIndex 时,您正在更新其模型,然后该模型会通知每个 ListSelectionListener。如果您可以“默默地”更新模型而不让任何人知道,那就不是 MVC。

This is expected behaviour.

From Model-View-Controller [Wikipedia]:

In event-driven systems, the model
notifies observers (usually views)
when the information changes so that
they can react.

So, when you call setSelectedIndex on the JList, you are updating its model, which then notifies each ListSelectionListener. It wouldn't be MVC if you could "silently" update a model without letting anyone know.

ぶ宁プ宁ぶ 2024-10-19 14:14:00

Swing 不完全是 MVC,但它有 MVC 的根源(区别在于,Swing 中的视图和控制器比其他 MVC 中的关系更密切,请参见 Swing 架构 了解更多详细信息)。

但这似乎不是您面临的问题。听起来您必须在事件侦听器中验证事件类型,并决定是否忽略它:如果事件源自列表,请更改它。如果是由其他一些控件触发的,则不要触发。

Swing is not exactly MVC, but has it's roots in MVC ( the difference lays in the fact, the view and the controller in Swing are more closely related than in other MVC see Swing architecture for more details ).

But this may not seem to be the problem you're facing. It sounds like you have to validate in the event listeners, for the type of event and decide whether to ignore it or not: If the event was originated in the list do change it. If was triggered by some other control, do not.

月亮坠入山谷 2024-10-19 14:14:00

@dogbane 说的话。

但要解决此问题,您需要在侦听器期间添加某种状态检查,以查看该事件是否是您应该忽略的事件。 ListSelectionEvent 有一个 getValueAdjusting() 方法,但这主要是内部方法。你需要做的就是自己模拟一下。

因此,例如,当您从外部选择更新列表时,您将拥有类似...的代码

try {
    setSelectionAdjusting(true);
    /* ... your old update code ... */
} finally {
    setSelectionAdjusting(false);
}

,并且在 ListSelectionListenerEvent 的更新代码中,

public void valueChanged(ListSelectionEvent e) {
    if (!isSelectionAdjusting()) {
        /* ... do what you did before ...*/
    }
}

范围和访问问题将作为读者的练习。您必须编写 setSelectionAdjusting,并可能将其设置在其他对象上。

What @dogbane said.

But to fix the problem you need to add some sort of a state check during the listener to see if the event is one you should ignore. The ListSelectionEvent has a getValueAdjusting() method, but that is mostly internal. What you need to do is simulate it yourself.

So for example when you update the list from an external selection you would have code like...

try {
    setSelectionAdjusting(true);
    /* ... your old update code ... */
} finally {
    setSelectionAdjusting(false);
}

and in the update code for the ListSelectionListenerEvent

public void valueChanged(ListSelectionEvent e) {
    if (!isSelectionAdjusting()) {
        /* ... do what you did before ...*/
    }
}

Scoping and access issues are left as an exercise for the reader. You would have to write the setSelectionAdjusting, and possibly set it on other objects as well.

糖粟与秋泊 2024-10-19 14:14:00

我仍然觉得这里在概念上有问题。

我表示理解,但考虑一下您没有一个简单的 JList 来观察 ListSelectionModel,这可能会有所帮助。相反,您有一个 JList 和一些其他控件来观察混合选择模型。在 @Taisin 的示例中,混合体是扩展 DefaultListSelectionModelCustomSelectionModel 和允许无声的改变。兼容时,还可以共享模型,如此问题和建议中所建议的那样。答案和这个<教程中的代码>SharedModelDemo

作为参考,此帖子引用了该文章使用 MVC 进行 Java SE 应用程序设计:应用程序设计问题,它更详细地解决了这个问题。

I still feel like there is something conceptually wrong here.

I empathize, but it may help to consider that you don't have a simple JList observing a ListSelectionModel. Instead, you have a JList and some-other-control observing a hybrid selection model. In @Taisin's example, the hybrid is a CustomSelectionModel that extends DefaultListSelectionModel and allows silent changes. When compatible, it's also possible to share a model, as suggested in this question & answer and this SharedModelDemo from the tutorial.

For reference, this thread cites the article Java SE Application Design With MVC: Issues With Application Design, which addresses the issue in more detail.

屌丝范 2024-10-19 14:14:00

我总是这样做:

    public class MyDialog extends JDialog {
        private boolean silentGUIChange = false;

    public void updateGUI {
        try {
            silenGUIChange = true;

            // DO GUI-Updates here:
            textField.setText("...");
            checkBox.setSelected (...);

        }
        finally {
            silentGUIChange = false;
        }
    }

    private void addListeners () {
        checkBox.addChangeListener (new ChangeListener () {
           public void stateChanged (ChangeEvent e) {
              if (silentGUIChange)
                 return;

              // update MODEL
              model.setValue(checkBox.isSelected());
          }
         });
    }

}

I always do like this:

    public class MyDialog extends JDialog {
        private boolean silentGUIChange = false;

    public void updateGUI {
        try {
            silenGUIChange = true;

            // DO GUI-Updates here:
            textField.setText("...");
            checkBox.setSelected (...);

        }
        finally {
            silentGUIChange = false;
        }
    }

    private void addListeners () {
        checkBox.addChangeListener (new ChangeListener () {
           public void stateChanged (ChangeEvent e) {
              if (silentGUIChange)
                 return;

              // update MODEL
              model.setValue(checkBox.isSelected());
          }
         });
    }

}
如歌彻婉言 2024-10-19 14:14:00

通常,侦听器的工作方式是每次它等待的事件发生时它都会“关闭”。如果我不得不推测这是你方面的误解。

Typically the way a listener works is it will "go off" every time the event that it is waiting for occurs. If I had to speculate it is a misunderstanding from your side of things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文