Есть ли простой способ изменить поведение элемента управления Java/Swing, когда он получает фокус?
-
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.