当Java/Swing控件获得焦点时,是否有一种简单的方法来更改其行为?

当Java/Swing控件获得焦点时,是否有一种简单的方法来更改其行为?,java,user-interface,swing,Java,User Interface,Swing,对于我使用过的大多数GUI,当包含文本的控件获得焦点时,将选择控件的全部内容。这意味着,如果您刚开始键入,您将完全替换以前的内容 示例:您有一个自旋控件,该控件的初始化值为零。您可以使用tab键单击它并键入“1”。控件中的值现在为1 对于Swing,这不会发生。控件中的文本未选中,克拉显示在现有文本的一端或另一端。继续上面的例子: 使用Swing JSpinner,当您点击旋转控件时,克拉位于左侧。键入“1”,控件中的值现在为10 这让我(和我的用户)陷入困境,我想改变它。更重要的是,我想对其进

对于我使用过的大多数GUI,当包含文本的控件获得焦点时,将选择控件的全部内容。这意味着,如果您刚开始键入,您将完全替换以前的内容

示例:您有一个自旋控件,该控件的初始化值为零。您可以使用tab键单击它并键入“1”。控件中的值现在为1

对于Swing,这不会发生。控件中的文本未选中,克拉显示在现有文本的一端或另一端。继续上面的例子:

使用Swing JSpinner,当您点击旋转控件时,克拉位于左侧。键入“1”,控件中的值现在为10

这让我(和我的用户)陷入困境,我想改变它。更重要的是,我想对其进行全局更改,使新行为适用于JTextField、JPasswordField、JFormattedTextField、JTextArea、JComboBox、JSpinner等。我找到的唯一方法是在每个控件中添加一个FocusAdapter,并重写focusGoverd()方法来做正确的事情[tm]

必须有一个更简单,更不脆弱的方法。求你了

编辑:此特定案例的一条附加信息。我正在使用的表单是使用Idea的表单设计器生成的。这意味着我通常不会编写代码来创建组件。这是可能的告诉想法,你们想自己创建它们,但这是一个麻烦,我想避免


座右铭:所有优秀的程序员基本上都是懒惰的。

我知道的唯一方法就是创建一个FocusListener并将其附加到组件上。如果您希望它的焦点侦听器对应用程序中的所有组件都是全局的,那么您可以考虑使用面向方面编程(AOP)。使用AOP,您可以对其进行一次编码,并将焦点侦听器应用于应用程序中实例化的所有组件,而无需复制和粘贴组件。整个应用程序中的addFocusListener(侦听器)代码

您的方面必须截获JComponent(或您希望将此行为添加到的子类)的创建,并将焦点侦听器添加到新创建的实例中。AOP方法比将FocusListener复制并粘贴到整个代码中要好,因为您将所有内容都保存在一段代码中,并且在您决定更改全局行为(如删除JSpinner的侦听器)时不会造成维护噩梦


有很多AOP框架可供选择。我喜欢它,因为它是100%纯Java,但您可能想看看。

当我过去需要它时,我已经创建了组件的子类,我也想添加“自动清除”功能。例如:

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是我发现的最常用的方法

另一个选项是在具有ContainerLister的顶级容器上监视ContainerEvents,以检测是否存在新的小部件,并根据已添加的小部件添加相应的焦点侦听器。(例如:如果容器事件是由添加TextField引起的,那么添加一个焦点侦听器,该侦听器知道如何选择TextField中的所有文本,依此类推。)如果添加了容器,那么还需要递归地将ContainerListener添加到新的子容器中


无论哪种方式,您都不需要在实际的UI代码中使用焦点侦听器——这一切都将在更高的级别上进行处理。

我自己也没有尝试过这一点(只是在不久前涉猎过),但您可能可以使用以下方法获得当前的焦点组件: KeyboardFocusManager(有一个静态方法getCurrentKeyboardFocusManager()) 向其添加PropertyChangeListener的过程。
从这里,您可以确定该组件是否为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和所有其他文本编辑组件中。这还允许类将全选添加到可编辑的组合框和jspinner中,您对文本编辑器组件的控制可能会受到更大的限制。可以添加方便的方法:

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);
  }
}

另外,可能不需要remove listener方法,因为该侦听器不包含任何其他实例的外部引用,但可以添加它们以使代码审查更顺利。

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

它起作用了

我找到了一个满足我需求的解决方案。这是作为答案发布的。+1根据我的经验,如果您有许多控件和/或使用可视化表单设计器,这是最好的解决方案。+1用于
setText(getText())
技巧;我已经为自己解决了所有其他问题,但仍然不起作用!