Когда я получаю ChangeEvent
от своего JSpinner
, я хотел бы определить, использовал ли пользователь стрелки для увеличения/уменьшения числового значения или напрямую набрал новое число.
Что было бы лучшим подходом для этого?
Обновлено: на данный момент мое ненадежное решение состоит в том, чтобы просто сохранить последнее измененное значение JSpinner, и если новое значение изменения +/- равно значению шага, то я предполагаю, что пользователь щелкнул стрелку. Это работает, за исключением случаев, когда пользователь ввел значение, равное (oldValue +/- step).
Обновлено: почему? Я хочу воспроизвести поведение, обнаруженное в редакторах Midi нескольких известных DAW. JSpinner представляет скорость (0-127) выбранных нот. Он показывает скорость первой выбранной ноты. Обычно ноты различаются по скорости. Когда вы увеличиваете с помощью стрелки, вы хотите увеличить все выбранные ноты на одинаковую величину. Когда вы вводите новое значение, вы хотите, чтобы все скорости были сброшены на это значение.
Хм, или, может быть, вы не можете использовать привязки клавиш ... Я еще не заставил это работать.
ChangeEvent не имеет этой информации. Почему вы решили, что вам нужно это знать?
почему? ..........
Чтобы ответить на вопрос в заголовке, расширьте JSpinner
, ChangeListener
и ChangeEvent
, чтобы включить информацию, которую вы ищете. Удачи.
Ответил почему. @GilbertLeBlanc да, но моя проблема в том, как получить информацию, которая будет помещена в расширенное событие ChangeEvent.
Изучите исходный код класса JSpinner
и узнайте, что вам нужно изменить.
Теперь, когда вы упомянули, почему, возможно, сработает использование двух JSpinners
, одного для увеличения и одного для выбора.
Различение триггера изменения значения не поддерживается — единственное событие, связанное со значением, инициированное JSpinner, — это ChangeEvent, который не несет никакого состояния, кроме источника. Нам нужен другой тип слушателя или комбинация слушателей.
Первая мысль: послушать изменения текстового поля редактора, т.е. прослушиватель действий или прослушиватель свойствChangeListener для свойства value. Это не работает, в основном потому, что
Вторая мысль: копаться в деталях реализации, чтобы зацепиться за действие, запускаемое кнопками со стрелками.
Идея:
Клиентский код изменит changeListener твика, чтобы он действовал в соответствии с флагом по мере необходимости.
Некоторый код, выполняющий настройку (осторожно: формально не тестировался!):
public static class SpinnerTweaker {
private JSpinner spinner;
private boolean wasButtonAction;
private Object oldValue;
public SpinnerTweaker(JSpinner spinner) {
this.spinner = spinner;
AbstractAction incrementDelegate = createDelegate("increment");
spinner.getActionMap().put("increment", incrementDelegate);
AbstractAction decrementDelegate = createDelegate("decrement");
spinner.getActionMap().put("decrement", decrementDelegate);
// replacing arrow button's action
Component[] components = spinner.getComponents();
for (Component component : components) {
if (component instanceof JButton) {
if (component.getName() == "Spinner.nextButton") {
ActionListener[] actions = ((JButton) component).getActionListeners();
ActionListener uiAction = actions[0];
((JButton) component).removeActionListener(uiAction);
((JButton) component).addActionListener(incrementDelegate);
}
if (component.getName() == "Spinner.previousButton") {
ActionListener[] actions = ((JButton) component).getActionListeners();
ActionListener uiAction = actions[0];
((JButton) component).removeActionListener(uiAction);
((JButton) component).addActionListener(decrementDelegate);
}
}
}
spinner.addChangeListener(e -> {
if (wasButtonAction) {
System.out.println("value changed by button: " + spinner.getValue());
} else {
System.out.println("value changed by editing: " + spinner.getValue());
}
wasButtonAction = false;
});
}
protected AbstractAction createDelegate(String actionCommand) {
// hooking into original button action to set button flag
AbstractAction action = (AbstractAction) spinner.getActionMap().get(actionCommand);
AbstractAction delegate = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
oldValue = spinner.getValue();
wasButtonAction = true;
action.actionPerformed(e);
// hit min/max - TBD: needs testing!
if (oldValue.equals(spinner.getValue())) {
wasButtonAction = false;
}
}
};
return delegate;
}
}
Протестировал, работает. Только один сюрприз: например. значение=2. Вы вводите 4, затем прямо нажимаете стрелку вверх (не нажимайте ввод). Значение становится равным 5, если wasButton=false. Что не совсем неправильно, но неожиданно для меня.
хм.. в этом случае тоже можно было бы ожидать, что wasButton true, звучит как неполная !логика в этом твике - завтра постараюсь найти дыру
Решение @kleopatra работает, но я нашел более простое решение.
Хитрость в том, что commitEdit()
вызывается JSpinner только внутри, когда изменение является результатом действия увеличения или уменьшения.
public class Spinner2 extends JSpinner
{
// To be checked by ChangeListener after receiving the ChangeEvent
public boolean wasManualEdit=true;
@Override
public void commitEdit() throws ParseException
{
wasManualEdit = false;
super.commitEdit();
}
@Override
protected void fireStateChanged()
{
super.fireStateChanged();
wasManualEdit = true;
}
}
1. Не используйте KeyListener. 2. Вы можете использовать привязки клавиш. 3. Почему вы хотите это сделать?