使用许多子组件检测焦点丢失
Detect focuslost with many subcomponents
我想创建一个可扩展面板(为特定任务容纳多个 UI 元素)。面板可以通过用户操作展开,只要面板中的任何子组件具有焦点,它就应该保持展开状态。当用户将焦点放在面板外的某处时,它应该关闭。
有没有简单的方法
- 当焦点移出任何子组件时获取 focuslost 事件
- 或者查询面板的子组件是否还有焦点?
或者我是否需要 register/check 关注每个子组件?
(因为这应该是一个通用的可用面板,所以我需要一种独立于子组件的特定层次结构的通用方法。)
根据 the official tutorials,将焦点侦听器分组到多个 Component
的推荐方法是在当前 KeyboardFocusManager
上安装一个 PropertyChangeListener
。例如:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Main {
private static void createAndShowGUI() {
final int rows = 5, cols = 5;
final JPanel focusables = new JPanel(new GridLayout(0, cols, 10, 10));
for (int row = 0; row < rows; ++row)
for (int col = 0; col < cols; ++col)
focusables.add(new JTextField("Focusable", 10));
final JPanel nonFocusable = new JPanel(); //FlowLayout.
nonFocusable.add(new JTextField("Click here to change focus"));
final JPanel contents = new JPanel(new BorderLayout());
contents.add(focusables, BorderLayout.CENTER);
contents.add(nonFocusable, BorderLayout.LINE_END);
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
final Object newValue = evt.getNewValue();
if (newValue instanceof Component
&& SwingUtilities.isDescendingFrom((Component) newValue, focusables)) //If 'focusables' is a parent of newValue...
focusables.setBackground(Color.GREEN); //Then focus is back on our components...
else
focusables.setBackground(Color.RED); //Else the focus is not on our components (it may even be another application).
}
});
final JFrame frame = new JFrame("Multi focus lost");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
根据教程:
The property change listener is notified of every change involving the focus...
如何使用上面的例子:
- 运行 它,您可以在每个
JTextField
中单击以使其成为焦点。
- 如果当前聚焦的
JTextField
的 parent 有一个我们指定的 Component
(即所需 [= 的 parent JPanel
14=]s 在这种情况下,命名为 focusables
) 然后 parent 的背景颜色变为绿色,否则为红色。但是你显然可以实现你的逻辑而不是改变背景颜色。
以上示例的工作原理:
- 我们在当前
KeyboardFocusManager
上为 属性 focusOwner
安装了一个 PropertyChangeListener
,这将使我们仅在任何 Component
应用程序更改其 focus-owner 属性(即更改焦点 Component
会触发此类事件)。我们可以安装一个 PropertyChangeListener
这样我们就可以收到各种事件的通知,但是 focusOwner
是我们唯一感兴趣的,所以我们让系统知道。有关您可以通过焦点事件跟踪的所有属性的列表,请滚动到 bottom of the tutorial.
- 在
PropertyChangeListener
实现中,我们只需要检查新的焦点所有者是否为其 parent 设置了 focusables
面板。我们可以通过从焦点所有者向上遍历 Component
层次结构来检查这一点,或者只调用 SwingUtilities#isDescendingFrom
这是用于此目的的便捷方法。如果我们正在监听所有焦点事件,那么我们还必须手动过滤 属性 focusOwner
(通过检查 PropertyChangeEvent#getPropertyName
return)。
请注意,当我们离开应用程序时,我们想要的 Component
的焦点会丢失(但会在 return 时返回)。
另请注意,为简单起见,上例中 Component
的层次结构只有一层深(即,所需的可聚焦 Component
是 [= 的直接 children 19=] 面板),但我们可以将其更改为我们想要的任何深度,只要所需的 Component
沿着它们的路径都具有相同的 parent(必须不同于 parent non-desired 个)。
我能想到的唯一例外是,如果您通常混合使用 parent Container
,例如 JPanel
直接 children一个想要的 Component
和一个 non-desired。在这种情况下,您必须在 PorpertyChangeListener
中单独检查每个 Component
或为每个所需的 Component
实施 FocusListener
。关于如何实现 FocusListener
的官方教程可以在 here.
找到
我想创建一个可扩展面板(为特定任务容纳多个 UI 元素)。面板可以通过用户操作展开,只要面板中的任何子组件具有焦点,它就应该保持展开状态。当用户将焦点放在面板外的某处时,它应该关闭。
有没有简单的方法
- 当焦点移出任何子组件时获取 focuslost 事件
- 或者查询面板的子组件是否还有焦点?
或者我是否需要 register/check 关注每个子组件? (因为这应该是一个通用的可用面板,所以我需要一种独立于子组件的特定层次结构的通用方法。)
根据 the official tutorials,将焦点侦听器分组到多个 Component
的推荐方法是在当前 KeyboardFocusManager
上安装一个 PropertyChangeListener
。例如:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Main {
private static void createAndShowGUI() {
final int rows = 5, cols = 5;
final JPanel focusables = new JPanel(new GridLayout(0, cols, 10, 10));
for (int row = 0; row < rows; ++row)
for (int col = 0; col < cols; ++col)
focusables.add(new JTextField("Focusable", 10));
final JPanel nonFocusable = new JPanel(); //FlowLayout.
nonFocusable.add(new JTextField("Click here to change focus"));
final JPanel contents = new JPanel(new BorderLayout());
contents.add(focusables, BorderLayout.CENTER);
contents.add(nonFocusable, BorderLayout.LINE_END);
KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
final Object newValue = evt.getNewValue();
if (newValue instanceof Component
&& SwingUtilities.isDescendingFrom((Component) newValue, focusables)) //If 'focusables' is a parent of newValue...
focusables.setBackground(Color.GREEN); //Then focus is back on our components...
else
focusables.setBackground(Color.RED); //Else the focus is not on our components (it may even be another application).
}
});
final JFrame frame = new JFrame("Multi focus lost");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
根据教程:
The property change listener is notified of every change involving the focus...
如何使用上面的例子:
- 运行 它,您可以在每个
JTextField
中单击以使其成为焦点。 - 如果当前聚焦的
JTextField
的 parent 有一个我们指定的Component
(即所需 [= 的 parentJPanel
14=]s 在这种情况下,命名为focusables
) 然后 parent 的背景颜色变为绿色,否则为红色。但是你显然可以实现你的逻辑而不是改变背景颜色。
以上示例的工作原理:
- 我们在当前
KeyboardFocusManager
上为 属性focusOwner
安装了一个PropertyChangeListener
,这将使我们仅在任何Component
应用程序更改其 focus-owner 属性(即更改焦点Component
会触发此类事件)。我们可以安装一个PropertyChangeListener
这样我们就可以收到各种事件的通知,但是focusOwner
是我们唯一感兴趣的,所以我们让系统知道。有关您可以通过焦点事件跟踪的所有属性的列表,请滚动到 bottom of the tutorial. - 在
PropertyChangeListener
实现中,我们只需要检查新的焦点所有者是否为其 parent 设置了focusables
面板。我们可以通过从焦点所有者向上遍历Component
层次结构来检查这一点,或者只调用SwingUtilities#isDescendingFrom
这是用于此目的的便捷方法。如果我们正在监听所有焦点事件,那么我们还必须手动过滤 属性focusOwner
(通过检查PropertyChangeEvent#getPropertyName
return)。
请注意,当我们离开应用程序时,我们想要的 Component
的焦点会丢失(但会在 return 时返回)。
另请注意,为简单起见,上例中 Component
的层次结构只有一层深(即,所需的可聚焦 Component
是 [= 的直接 children 19=] 面板),但我们可以将其更改为我们想要的任何深度,只要所需的 Component
沿着它们的路径都具有相同的 parent(必须不同于 parent non-desired 个)。
我能想到的唯一例外是,如果您通常混合使用 parent Container
,例如 JPanel
直接 children一个想要的 Component
和一个 non-desired。在这种情况下,您必须在 PorpertyChangeListener
中单独检查每个 Component
或为每个所需的 Component
实施 FocusListener
。关于如何实现 FocusListener
的官方教程可以在 here.