有没有简单的方法可以在Java / Swing控件获得焦点时更改其行为?

时间:2020-03-05 18:54:24  来源:igfitidea点击:

对于大多数我使用的GUI,当包含文本的控件成为焦点时,将选择该控件的全部内容。这意味着,如果我们只是开始输入内容,则将完全替换之前的内容。

示例:我们具有旋转控件,该控件已初始化为零值。我们制表符并键入" 1",控件中的值现在为1.

使用Swing,这不会发生。没有选择控件中的文本,并且克拉出现在现有文本的一端或者另一端。继续上面的示例:

使用Swing JSpinner,当我们跳至旋转控件时,克拉在左侧。我们键入" 1",控件中的值现在为10.

这将我(和我的用户)推上了墙,我想对其进行更改。更重要的是,我想全局更改它,以便新行为适用于JTextField,JPasswordField,JFormattedTextField,JTextArea,JComboBox,JSpinner等。我发现这样做的唯一方法是将FocusAdapter添加到每个控件,并覆盖focusGained()方法以执行正确的操作[tm]。

有一种更简单,更不易碎的方法。请?

编辑:针对此特定情况的另一条信息。我正在使用的表单是使用Idea的表单设计器生成的。这意味着我通常实际上并不编写代码来创建组件。可以告诉Idea我们想自己创建它们,但这是我要避免的麻烦。

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

解决方案

回答

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

方面将不得不拦截JComponent(或者我们要向其添加此行为的子类)的创建,并将焦点侦听器添加到新创建的实例。 AOP方法比将FocusListener复制并粘贴到整个代码中更好,因为我们将其全部保存在单个代码中,并且一旦决定更改全局行为(例如删除针对该对象的侦听器),就不会造成维护方面的噩梦。 JSpinners。

有许多可供选择的AOP框架。我喜欢JBossAOP,因为它是100%纯Java,但是我们可能想看一下AspectJ。

回答

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

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在顶级容器上监视ContainerEvent,以检测是否存在新的小部件,并基于已添加的小部件添加相应的焦点侦听器。 (例如:如果容器事件是由添加TextField引起的,则添加一个焦点侦听器,该侦听器知道如何选择TextField中的所有文本,依此类推。)如果添加了Container,则需要递归添加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和所有其他文本编辑组件中。这还允许该类将全选添加到可编辑的组合框和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);
  }
}

同样,由于侦听器不包含对任何其他实例的外部引用,因此可能不需要使用remove侦听器方法,但是可以添加它们,以使代码审查更加顺畅。

回答

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

有用!