有没有一种简单的方法,在Java/Swing控件获得焦点时改变其行为?

7
对于我使用过的大多数 GUI,当包含文本的控件获得焦点时,控件的全部内容都会被选中。这意味着如果您只是开始键入,您将完全替换以前的内容。
例如:您有一个旋转控件,它初始化为值零。您切换到它并键入“1”,控件中的值现在为1。
但是,Swing 不会发生这种情况。控件中的文本不被选择,插入符号出现在现有文本的一端或另一端。继续上面的例子:
对于 Swing JSpinner,当您切换到旋转控件时,插入符号位于左侧。您键入“1”后,控件中的值现在为10。
这让我(和我的用户)非常不爽,我想改变它。更重要的是,我想全局更改它,使新的行为适用于 JTextField、JPasswordField、JFormattedTextField、JTextArea、JComboBox、JSpinner 等等。目前唯一找到的解决方法是为每个控件添加 FocusAdapter 并覆盖 focusGained() 方法以实现正确的操作。
难道没有更简单、更可靠的方法吗?

我找到了一个满足我的需求的解决方案。 它作为一个答案发布了。 - Dale Wilson
5个回答

2

过去我需要这样的功能时,我会创建组件的子类来添加“自动清除”功能。例如:

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中的所有文本等)。如果添加了一个容器,则需要递归地将ContainerListener添加到该新子容器中。
无论哪种方式,您都不需要在实际UI代码中混合使用焦点侦听器 - 一切都会在更高级别上处理。

2
可以编写一个单独的类,将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);
  }
}

此外,删除监听器方法可能是不必要的,因为监听器对任何其他实例都没有外部引用,但是可以添加它们以使代码审查更顺利。

2

我自己没有尝试过这个方法(只是之前稍微涉猎过),但你可以使用KeyboardFocusManager(有一个静态方法getCurrentKeyboardFocusManager())来获取当前焦点组件,然后添加一个PropertyChangeListener到它上面。从那里开始,你就可以找出组件是否为JTextComponent并选择所有文本。


在我的经验中,如果您有许多控件和/或使用可视化表单设计器,则+1是最佳解决方案。 - Daniel Rikowski

1

阅读到目前为止的回复(谢谢!),我将最外层的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);
            }
        }
    }
}

它可以工作!


对于“setText(getText())”技巧,我给出加1分;其他的我都自己解决了,但还是不起作用! - Huw Walters

0
我知道的唯一方法是创建一个FocusListener并将其附加到您的组件上。如果您希望此FocusListener对应用程序中的所有组件都是全局的,您可以考虑使用面向方面编程(AOP)。使用AOP,您可以编写一次代码,并将您的焦点侦听器应用于应用程序中实例化的所有组件,而无需在整个应用程序中复制和粘贴component.addFocusListener(listener)代码。
您的方面必须拦截JComponent(或您想要添加此行为的子类)的创建,并将焦点侦听器添加到新创建的实例中。与将FocusListener复制并粘贴到整个代码中相比,AOP方法更好,因为您将所有内容保留在单个代码片段中,并且在决定更改全局行为(例如删除JSpinners的侦听器)时不会创建维护噩梦。
有许多AOP框架可供选择。我喜欢JBossAOP,因为它是100%纯Java,但您可能想看看AspectJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接