有没有一种简单的方法可以在 Java/Swing 控件获得焦点时更改其行为?
-
09-06-2019 - |
题
对于我使用过的大多数 GUI,当包含文本的控件获得焦点时,将选择该控件的全部内容。这意味着如果您刚开始输入,就会完全替换以前的内容。
例子:您拥有用零值初始化的旋转控件。您用 Tab 键选中它并输入“1”,控件中的值现在为 1。
使用 Swing,这种情况就不会发生。未选择控件中的文本,并且克拉出现在现有文本的一端或另一端。继续上面的例子:
使用 Swing JSpinner,当您按 Tab 键切换到旋转控件时,克拉位于左侧。您键入“1”,控件中的值现在为 10。
这让我(和我的用户)感到厌烦,我想改变它。更重要的是,我想在全局范围内更改它,以便新行为适用于 JTextField、JPasswordField、JFormattedTextField、JTextArea、JComboBox、JSpinner 等。我发现执行此操作的唯一方法是向每个控件添加 FocusAdapter 并重写 focusGained() 方法以执行正确的操作[tm]。
一定有一种更简单、更不脆弱的方法。请?
编辑:针对这一特殊案例的另一条信息。我正在使用的表单是使用 Idea 的表单设计器生成的。这意味着我通常不会实际编写代码来创建组件。可以告诉 Idea 你想自己创建它们,但这是我想避免的麻烦。
座右铭:所有优秀的程序员基本上都是懒惰的。
解决方案 4
阅读到目前为止的回复后(谢谢!)我将最外面的 JPanel 传递给以下方法:
void addTextFocusSelect(JComponent component){
if(component instanceof JTextComponent){
component.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent event) {
super.focusGained(event);
JTextComponent component = (JTextComponent)event.getComponent();
// a trick I found on JavaRanch.com
// Without this, some components don't honor selectAll
component.setText(component.getText());
component.selectAll();
}
});
}
else
{
for(Component child: component.getComponents()){
if(child instanceof JComponent){
addTextFocusSelect((JComponent) child);
}
}
}
}
有用!
其他提示
当我过去需要这个时,我也创建了我想要添加“自动清除”功能的组件的子类。例如:
public class AutoClearingTextField extends JTextField {
final FocusListener AUTO_CLEARING_LISTENER = new FocusListener(){
@Override
public void focusLost(FocusEvent e) {
//onFocusLost(e);
}
@Override
public void focusGained(FocusEvent e) {
selectAll();
}
};
public AutoClearingTextField(String string) {
super(string);
addListener();
}
private void addListener() {
addFocusListener(AUTO_CLEARING_LISTENER);
}
}
最大的问题是我还没有找到一种“好的”方法来获取所有标准构造函数而不编写重写。添加它们并强制调用 addListener 是我发现的最通用的方法。
另一种选择是使用 ContainerListeer 监视顶级容器上的 ContainerEvents,以检测新小部件的存在,并根据已添加的小部件添加相应的焦点侦听器。(例如:如果容器事件是由添加 TextField 引起的,则添加一个焦点侦听器,该侦听器知道如何选择 TextField 中的所有文本,依此类推。)如果添加了 Container,则需要递归地将 ContainerListener 添加到新的容器中子容器也是如此。
无论哪种方式,您都不需要在实际的 UI 代码中使用焦点侦听器——这一切都将在更高的级别上得到处理。
我自己还没有尝试过(前一段时间才涉足过),但您可能可以通过使用以下方式获取当前聚焦的组件:keyboardFocusManager(有一个静态方法getCurrentKeyboardFocusManager())向其添加属性changelistener。从那里,您可以查明该组件是否是 JTextComponent 并选择所有文本。
可以编写一个单独的类,将 FocusListener 附加到所需的文本字段。焦点侦听器要做的就是在获得焦点时在文本小部件上调用 selectAll() 。
public class SelectAllListener implements FocusListener {
private static INSTANCE = new SelectAllListener();
public void focusLost(FocusEvent e) { }
public void focusGained(FocusEvent e) {
if (e.getSource() instanceof JTextComponent) {
((JTextComponent)e.getSource()).selectAll();
}
};
public static void addSelectAllListener(JTextComponent tc) {
tc.addFocusListener(INSTANCE);
}
public static void removeSelectAllListener(JTextComponent tc) {
tc.removeFocusListener(INSTANCE);
}
}
通过接受 JTextComponent 作为参数,可以将此行为直接添加到 JTextArea、JPasswordField 和所有其他文本编辑组件。这还允许类将全选添加到可编辑组合框和 JSpinners,其中您对文本编辑器组件的控制可能更加有限。可以添加便捷方法:
public static void addSelectAllListener(JSpinner spin) {
if (spin.getEditor() instanceof JTextComponent) {
addSelectAllListener((JTextComponent)spin.getEditor());
}
}
public static void addSelectAllListener(JComboBox combo) {
JComponent editor = combo.getEditor().getEditorComponent();
if (editor instanceof JTextComponent) {
addSelectAllListener((JTextComponent)editor);
}
}
此外,删除侦听器方法可能是不需要的,因为侦听器不包含对任何其他实例的外部引用,但可以添加它们以使代码审查更加顺利。
我知道的唯一方法是创建一个 FocusListener 并将其附加到您的组件。如果您希望此 FocusListener 对于应用程序中的所有组件都是全局的,您可以考虑使用面向方面编程 (AOP)。使用 AOP,可以编写一次代码,并将焦点侦听器应用于应用程序中实例化的所有组件,而无需复制并粘贴 组件.addFocusListener(监听器) 整个应用程序中的代码..
您的方面必须拦截 JComponent(或您想要添加此行为的子类)的创建,并将焦点侦听器添加到新创建的实例。AOP 方法比将 FocusListener 复制并粘贴到整个代码中更好,因为您将所有内容都保存在单个代码中,并且一旦您决定更改全局行为(例如删除监听器),就不会造成维护噩梦。 JSpinners。