Есть ли простой способ изменить поведение элемента управления Java/Swing, когда он получает фокус?

StackOverflow https://stackoverflow.com/questions/66455

  •  09-06-2019
  •  | 
  •  

Вопрос

В большинстве графических интерфейсов, которые я использовал, когда элемент управления, содержащий текст, получает фокус, выбирается все содержимое элемента управления.Это означает, что если вы просто начнете печатать, вы полностью замените прежнее содержимое.

Пример:У вас есть управление вращением, которое инициализируется нулевым значением.Вы переходите к нему и вводите «1». Значение элемента управления теперь равно 1.

С Swing этого не происходит.Текст в элементе управления не выделен, и карат появляется в том или ином конце существующего текста.Продолжая приведенный выше пример:

При использовании Swing JSpinner при переходе к элементу управления вращением карат находится слева.Вы вводите «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 — это самый общий подход, который я нашел.

Другой вариант — отслеживать ContainerEvents в контейнере верхнего уровня с помощью ContainerListeer, чтобы обнаружить наличие новых виджетов, и добавить соответствующий прослушиватель фокуса на основе добавленных виджетов.(например:если событие контейнера вызвано добавлением TextField, добавьте прослушиватель фокуса, который знает, как выбрать весь текст в TextField и т. д.). Если добавлен контейнер, вам необходимо рекурсивно добавить ContainerListener к этому новому также подконтейнер.

В любом случае вам не придется возиться с прослушивателями фокуса в реальном коде пользовательского интерфейса — обо всем этом позаботятся на более высоком уровне.

Я сам не пробовал это делать (только баловался этим некоторое время назад), но вы, вероятно, можете получить текущий сфокусированный компонент, используя: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);
  }
}

Кроме того, методы удаления прослушивателя, вероятно, не нужны, поскольку прослушиватель не содержит внешних ссылок на какие-либо другие экземпляры, но их можно добавить, чтобы сделать проверку кода более гладкой.

Единственный известный мне способ — создать FocusListener и прикрепить его к вашему компоненту.Если вы хотите, чтобы этот FocusListener был глобальным для всех компонентов вашего приложения, вы можете рассмотреть возможность использования аспектно-ориентированного программирования (AOP).С помощью AOP можно один раз закодировать его и применить прослушиватель фокуса ко всем компонентам, созданным в вашем приложении, без необходимости копировать и вставлять компонент.addFocusListener(прослушиватель) код во всем вашем приложении..

Ваш аспект должен будет перехватить создание JComponent (или подклассов, к которым вы хотите добавить это поведение) и добавить прослушиватель фокуса к вновь созданному экземпляру.Подход АОП лучше, чем копирование и вставка FocusListener во весь ваш код, поскольку вы храните все это в одном фрагменте кода и не создаете кошмар обслуживания, когда решите изменить свое глобальное поведение, например удалить прослушиватель для JSpinners.

Существует множество фреймворков АОП на выбор.Мне нравится ЖБоссАОП поскольку это 100% чистая Java, но вы можете взглянуть на АспектJ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top