比直接请求初始组件的焦点更有趣的是使用自定义的FocusTraversalPolicy实现通用机制来查找/配置默认值。它的api中已经有了基本概念,包括返回初始/默认组件的方法,例如:
```java
public abstract Component getDefaultComponent(Container aContainer)
```
返回要聚焦的默认组件。当进入以aContainer为根的新焦点遍历周期时,该组件将是第一个接收焦点的组件。
默认实现返回循环中的第一个组件,自定义实现可能会查找自定义指示器,例如客户端属性。简单的用法是将该策略设置为具有内容窗格中标记的框架,类似于:
```java
frame.setFocusTraversalPolicyProvider(true);
frame.setFocusTraversalPolicy(new MyCustomPolicy());
```
FocusTraversalPolicy policy = new LayoutFocusTraversalPolicy() {
@Override
public Component getDefaultComponent(Container aContainer) {
if (aContainer instanceof JComponent) {
JComponent parent = (JComponent) aContainer;
if (parent.getClientProperty("defaultFocus") instanceof Component) {
return (Component) parent.getClientProperty("defaultFocus");
}
}
if (aContainer instanceof RootPaneContainer) {
RootPaneContainer root = (RootPaneContainer) aContainer;
JComponent parent = (JComponent) root.getContentPane();
if (parent.getClientProperty("defaultFocus") instanceof Component) {
return (Component) parent.getClientProperty("defaultFocus");
}
}
return super.getDefaultComponent(aContainer);
}
};
JFrame frame = ...
JTextField field = new JTextField(20);
frame.add(field, BorderLayout.SOUTH);
((JComponent) frame.getContentPane()).putClientProperty("defaultFocus", field);
frame.setFocusTraversalPolicy(policy);
更加优雅的方法是默认使用此策略 - 可以通过将其安装为KeyboardFocusManager的默认策略来实现。有了这个,使用将会变得更简单:
- 在应用程序生命周期的早期安装自定义默认值
- 标记初始焦点组件(无需手动设置框架策略)
代码:
initializeDefaultFocusTraversalPolicy();
JFrame frame = ...
JTextField field = new JTextField(20);
frame.add(field, BorderLayout.SOUTH);
((JComponent) frame.getContentPane())
.putClientProperty(DelegatingFocusTraversalPolicy.DEFAULT_FOCUS_KEY, field);
如果没有一个小技巧,Swing就不会是Swing:
- 窗口的默认策略在创建时就被内部设置了 - 因此键盘焦点管理器必须在创建框架之前进行配置
- UIManager对于管理器的默认设置有自己的想法,事实上,在创建非常第一个框架时无条件地配置它(这将覆盖我们自己的默认设置)
解决方法是创建一个虚拟框架,然后设置策略:
public static void initializeDefaultFocusTraversalPolicy() {
new JFrame();
FocusTraversalPolicy p = KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalPolicy();
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.setDefaultFocusTraversalPolicy(new CustomFocusTraversalPolicy(p));
}
public static class CustomFocusTraversalPolicy extends FocusTraversalPolicy {
public static final String DEFAULT_FOCUS_KEY = "defaultFocus";
private FocusTraversalPolicy delegate;
public CustomFocusTraversalPolicy(FocusTraversalPolicy delegate) {
this.delegate = Contract.asNotNull(delegate, "the delegate must not be null");
}
@Override
public Component getDefaultComponent(Container container) {
if (container instanceof JComponent) {
JComponent parent = (JComponent) container;
if (parent.getClientProperty(DEFAULT_FOCUS_KEY) instanceof Component) {
return (Component) parent.getClientProperty(DEFAULT_FOCUS_KEY);
}
}
if (container instanceof RootPaneContainer) {
RootPaneContainer root = (RootPaneContainer) container;
JComponent parent = (JComponent) root.getContentPane();
if (parent.getClientProperty(DEFAULT_FOCUS_KEY) instanceof Component) {
return (Component) parent.getClientProperty(DEFAULT_FOCUS_KEY);
}
}
return delegate.getDefaultComponent(container);
}
@Override
public Component getComponentAfter(Container aContainer,
Component aComponent) {
return delegate.getComponentAfter(aContainer, aComponent);
}
@Override
public Component getComponentBefore(Container aContainer,
Component aComponent) {
return delegate.getComponentBefore(aContainer, aComponent);
}
@Override
public Component getFirstComponent(Container aContainer) {
return delegate.getFirstComponent(aContainer);
}
@Override
public Component getLastComponent(Container aContainer) {
return delegate.getLastComponent(aContainer);
}
}
JComponent.requestFocusInWindow()
。 - Andrew ThompsontextField1.requestFocusInWindow();
吗? - paulsm4