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

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

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

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

С помощью Swing JSpinner, когда вы переходите на элемент управления вращением, карат находится слева. Вы вводите «1», и значение в элементе управления теперь равно 10.

Это приводит меня (и моих пользователей) к стене, и я бы хотел это изменить. Что еще более важно, я хотел бы изменить его глобально, чтобы новое поведение применялось к JTextField, JPasswordField, JFormattedTextField, JTextArea, JComboBox, JSpinner и так далее. Я нашел единственный способ сделать это, чтобы добавить FocusAdapter к каждому элементу управления и переопределить метод focusGained () для выполнения правильных действий [tm].

Должен быть более простой и менее хрупкий способ. Пожалуйста?

РЕДАКТИРОВАТЬ: один дополнительный кусок информации для этого конкретного случая. Форма, с которой я работаю, была сгенерирована с помощью дизайнера форм Idea. Это означает, что я обычно не пишу код для создания компонентов. Можно сказать Idea, что вы хотите создать их самостоятельно, но я бы хотел избежать этого.

Девиз: Все хорошие программисты в основном ленивы.

Ответы [ 5 ]

2 голосов
/ 16 сентября 2008

Можно написать отдельный класс, который присоединяет 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 и всем другим компонентам редактирования текста. Это также позволяет классу добавить select all в редактируемые поля со списком и 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 голосов
/ 16 сентября 2008

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

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 в этот новый субконтейнер.

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

2 голосов
/ 16 сентября 2008

Я сам не пробовал это (только баловался этим некоторое время назад), но вы, вероятно, можете получить текущий фокусированный компонент, используя: KeyboardFocusManager (есть статический метод getCurrentKeyboardFocusManager ()) добавление PropertyChangeListener к нему. Оттуда вы можете узнать, является ли компонент JTextComponent, и выделить весь текст.

1 голос
/ 17 сентября 2008

После прочтения ответов (спасибо!) Я передал самый внешний 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);
            }
        }
    }
}

Работает!

0 голосов
/ 16 сентября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...