java - 是否可以避免保留对听众的引用?
问题描述
我正在尝试创建一个“被动视图”的想法,其中用户操作会触发侦听器,但应用程序本身不会。
考虑一个我需要监听 ComponentResized 事件的情况。当用户调整窗口大小时,我会做一些事情。但是一键按下也会调用setSize()
该组件的方法。当setSize
从程序调用时,我不希望监听器被解雇。但是当它来自我想要的用户操作时。
public class Example extends JFrame {
static boolean stopResizing = false;
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (stopResizing == true)
return;
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
stopResizing = true;
setSize(getSize().width + 15, getSize().height);
stopResizing = false;
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
在上面的示例中,当窗口调整大小时,会打印“resized”。但是当按钮被按下时,它也会被打印出来。我知道我可以remove
并且重新add
成为组件侦听器,但这可以避免吗?在注册了多个听众的“大”视图中,这将是痛苦的。
正如我通过遵循 的调用层次结构所看到的那样setSize
,事件发布在EventQueue
. 这就是布尔标志不起作用的原因。标志true
在事件触发之前变为。所以,也许这个问题可以派生为“我可以操纵(多么安全/可信)EventQueue?”。stopResizing = false
通过在发布火灾事件之前添加来操作它。
另一种选择可能是创建一个静态方法来迭代所有侦听器,删除它们,运行 a Runnable
(包含setSize
),然后该方法将它们重新添加上。但据我所知,粗暴地从组件中删除所有侦听器,也会删除 Swing 的内部侦听器,并且组件会出现意外行为。也许有一种方法可以在不保留引用的情况下将自定义(由我添加)侦听器与 Swing 的侦听器分开?
我试图将其添加stopResizing = false
到invokeLater
通话中,但它也不起作用。
请记住,这不仅仅是关于 ComponentListeners。它适用于任何类型的侦听器,适用于任何类型的组件。因此,我想让它“概括”它。
更新 即使我删除了侦听器,也会打印“调整大小”。
public class Example extends JFrame {
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
removeComponentListener(listener);
setSize(getSize().width + 15, getSize().height);
addComponentListener(listener);
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
我想避免 invokeLater 的原因是因为代码可能隐式如下所示:
removeComponentListener(listener);
setSize(getSize().width + 15, getSize().height); //I dont want to fire the listener
addComponentListener(listener);
setSize(getSize().width + 15, getSize().height); // I Want to fire the listener
解决方案
在花了 2 天时间阅读了多次 EventQueue 课程后,我想我解决了。解决方案似乎是一个SecondaryLoop,而后台线程等待所有事件都被调度(在我们的例子中包括组件事件)。
public class Example extends JFrame {
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
removeComponentListener(listener);
setSize(new Dimension(getSize().width + 1, getSize().height));
waitUntilAllEventsAreDispatched();
addComponentListener(listener);
setSize(new Dimension(getSize().width + 1, getSize().height)); //I want here to print
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
private EventQueue eventQueue() {
return Toolkit.getDefaultToolkit().getSystemEventQueue();
}
private void waitUntilAllEventsAreDispatched() {
SecondaryLoop secondaryLoop = eventQueue().createSecondaryLoop();
new Thread(() -> {
while (eventQueue().peekEvent() != null)
;
secondaryLoop.exit();
}).start();
secondaryLoop.enter();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
此示例将仅打印一次“RESIZED”。
正如预期的那样,它也适用于布尔标志:
ComponentAdapter listener = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
if (allListenersDisabled)
return;
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
allListenersDisabled = true;
setSize(new Dimension(getSize().width + 1, getSize().height));
waitUntilAllEventsAreDispatched();
allListenersDisabled = false;
setSize(new Dimension(getSize().width + 1, getSize().height)); //I want here to print
});
推荐阅读
- python - Django orm 删除重叠的 date_ranges
- r - r:使用重复循环创建有限大小的集群
- jmeter - 在一个 Jmeter 正则表达式中提取多个标签值
- django - graphene-django:没有“Meta.fields”或“Meta.exclude”的“Meta.model”自 0.15.0 以来已被弃用,现在已被禁止
- java - 单向一对多关联的条件查询,使用实体列表进行搜索
- angular - 如何在 ngRX 效果中测试 forkJoin
- python - 仅在包含两列 NaN 的那些行中填写 NaN
- reactjs - 在 Ant-design 表格列中添加按钮
- xml - 所有属性值的 XPath,而不仅仅是第一个?
- angular - 点击角度动态添加整个div