简体   繁体   English

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

[英]Is there an easy way to change the behavior of a Java/Swing control when it gets focus?

For most GUI's I've used, when a control that contains text gets the focus, the entire contents of the control are selected.对于我使用过的大多数 GUI,当包含文本的控件获得焦点时,控件的全部内容都会被选中。 This means if you just start typing, you completely replace the former contents.这意味着如果您刚开始输入,您将完全替换以前的内容。

Example: You have spin control that is initialized with the value zero.示例:您有使用零值初始化的自旋控制。 You tab to it and type "1" The value in the control is now 1.您选择它并键入“1”控件中的值现在是 1。

With Swing, this doesn't happen.对于 Swing,这不会发生。 The text in the control is not selected and the carat appears at one end or another of the existing text.控件中的文本未被选中,并且克拉出现在现有文本的一端或另一端。 Continuing the above example:继续上面的例子:

With a Swing JSpinner, when you tab to the spin control, the carat is at the left.使用 Swing JSpinner,当您选择旋转控件时,克拉在左侧。 You type "1" and the value in the control is now 10.您键入“1”,控件中的值现在是 10。

This drives me, (and my users) up a wall, and I'd like to change it.这让我(和我的用户)陷入困境,我想改变它。 Even more important, I'd like to change it globally so the new behavior applies to JTextField, JPasswordField, JFormattedTextField, JTextArea, JComboBox, JSpinner, and so on.更重要的是,我想全局更改它,以便新行为适用于 JTextField、JPasswordField、JFormattedTextField、JTextArea、JComboBox、JSpinner 等。 The only way I have found to do this to add a FocusAdapter to each control and override the focusGained() method to Do The Right Thing[tm].我发现这样做的唯一方法是将 FocusAdapter 添加到每个控件并覆盖 focusGained() 方法来做正确的事情[tm]。

There's gotta be an easier, and less fragile way.必须有一种更简单、更不脆弱的方法。 Please?请?

EDIT: One additional piece of information for this particular case.编辑:此特定案例的另一条信息。 The form I am working with was generated using Idea's form designer.我正在使用的表单是使用 Idea 的表单设计器生成的。 That means I normally don't actually write the code to create the components.这意味着我通常不会真正编写代码来创建组件。 It is possible to tell Idea that you want to create them yourself, but that's a hassle I'd like to avoid.可以告诉 Idea 您想自己创建它们,但这是我想避免的麻烦。

Motto: All good programmers are basically lazy.座右铭:所有优秀的程序员基本上都是懒惰的。

When I've needed this in the past, I've created subclasses of the components I wanted to add "auto-clearing" functionality too.当我过去需要这个时,我已经创建了我想添加“自动清除”功能的组件的子类。 eg:例如:

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

The biggest problem is that I haven't found a "good" way to get all the standard constructors without writing overrides.最大的问题是我还没有找到一种“好”的方法来获得所有标准构造函数而无需编写覆盖。 Adding them, and forcing a call to addListener is the most general approach I've found.添加它们并强制调用 addListener 是我发现的最通用的方法。

Another option is to watch for ContainerEvents on a top-level container with a ContainerListeer to detect the presence of new widgets, and add a corresponding focus listener based on the widgets that have been added.另一种选择是使用 ContainerListeer 监视顶级容器上的 ContainerEvents 以检测新小部件的存在,并根据已添加的小部件添加相应的焦点侦听器。 (eg: if the container event is caused by adding a TextField, then add a focus listener that knows how to select all the text in a TextField, and so on.) If a Container is added, then you need to recursively add the ContainerListener to that new sub-container as well. (例如:如果容器事件是由添加TextField引起的,那么添加一个焦点监听器,它知道如何select 一个TextField中的所有文本,等等。)如果添加了一个Container,那么就需要递归添加ContainerListener到那个新的子容器。

Either way, you won't need to muck about with focus listeners in your actual UI code -- it will all be taken care of at a higher level.无论哪种方式,您都不需要在实际的 UI 代码中处理焦点侦听器——这一切都将在更高级别上得到处理。

I haven't tried this myself (only dabbled in it a while ago), but you can probably get the current focused component by using: KeyboardFocusManager (there is a static method getCurrentKeyboardFocusManager()) an adding a PropertyChangeListener to it.我自己还没有尝试过(不久前才涉足它),但是您可能可以通过使用以下方法获得当前的焦点组件:KeyboardFocusManager(有一个 static 方法 getCurrentKeyboardFocusManager())并向其中添加一个 PropertyChangeListener。 From there, you can find out if the component is a JTextComponent and select all text.从那里,您可以确定该组件是否为 JTextComponent 和 select 所有文本。

A separate class that attaches a FocusListener to the desired text field can be written.可以编写将 FocusListener 附加到所需文本字段的单独 class。 All the focus listener would do is call selectAll() on the text widget when it gains the focus.焦点侦听器所做的就是在文本小部件获得焦点时调用 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);
  }
}

By accepting a JTextComponent as an argument this behavior can be added to JTextArea, JPasswordField, and all of the other text editing components directly.通过接受 JTextComponent 作为参数,可以将此行为直接添加到 JTextArea、JPasswordField 和所有其他文本编辑组件。 This also allows the class to add select all to editable combo boxes and JSpinners, where your control over the text editor component may be more limited.这也允许 class 将 select 全部添加到可编辑的组合框和 JSpinner 中,您对文本编辑器组件的控制可能会受到更多限制。 Convenience methods can be added:可以添加方便的方法:

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

Also, the remove listener methods are likely unneeded, since the listener contains no exterior references to any other instances, but they can be added to make code reviews go smoother.此外,可能不需要删除侦听器方法,因为侦听器不包含对任何其他实例的外部引用,但可以添加它们以使代码审查 go 更顺畅。

After reading the replies so far (Thanks:) I passed the outermost JPanel to the following method:在阅读了到目前为止的回复之后(谢谢:)我将最外层的 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);
            }
        }
    }
}

It works!有用!

The only way I know is to create a FocusListener and attach it to your component.我知道的唯一方法是创建一个 FocusListener 并将其附加到您的组件。 If you want it this FocusListener to be global to all components in your application you might consider using Aspect Oriented Programming (AOP).如果您希望此 FocusListener 对应用程序中的所有组件都是全局的,您可以考虑使用面向方面的编程 (AOP)。 With AOP is possible to code it once and apply your focus listener to all components instantiated in your app without having to copy-and-paste the component.addFocusListener(listener) code throughout your application..使用 AOP 可以编写一次代码并将焦点侦听器应用于应用程序中实例化的所有组件,而无需在整个应用程序中复制和粘贴component.addFocusListener(listener)代码。

Your aspect would have to intercept the creation of a JComponent (or the sub-classes you want to add this behaviour to) and add the focus listener to the newly created instance.您的切面必须拦截 JComponent 的创建(或要添加此行为的子类)并将焦点侦听器添加到新创建的实例。 The AOP approach is better than copy-and-pasting the FocusListener to your entire code because you keep it all in a single piece of code, and don't create a maintenance nightmare once you decide to change your global behavior like removing the listener for JSpinners. AOP 方法比将 FocusListener 复制并粘贴到整个代码中要好,因为您将其全部保存在一段代码中,并且一旦您决定更改全局行为(例如删除侦听器),就不会造成维护噩梦JSpinners。

There are many AOP frameworks out there to choose from.有许多 AOP 框架可供选择。 I like JBossAOP since it's 100% pure Java, but you might like to take a look at AspectJ .我喜欢JBossAOP ,因为它是 100% 纯 Java,但您可能想看看AspectJ

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM