590 |
ГЛАВА 18 |
JPanel buttons = new JPanel(); buttons.add(add); buttons.add(undo); buttons.add(redo); add(buttons, "South");
// выведем окно на экран setSize(400, 300); setVisible(true);
}
// класс, описывающий добавление в список
class ListAddUndoableEdit extends AbstractUndoableEdit { // модель и новый элемент
private DefaultListModel model; private Object element;
public ListAddUndoableEdit(DefaultListModel model, Object element) {
this.model = model; this.element = element;
}
@Override
public void undo() throws CannotUndoException { super.undo();
model.removeElement(element);
}
@Override
public void redo() throws CannotRedoException { super.redo();
model.addElement(element);
}
}
public static void main(String[] args) { SwingUtilities.invokeLater(
new Runnable() {
public void run() { new UndoListAdd(); } });
}
}
В примере в центре окна разместятся два списка одинакового размера. У них будет одна стандартная модель DefaultListModel, поначалу пустая. На юге окна у нас разместится панель с последовательным расположением, с тремя кнопками. Первая кнопка будет добавлять к модели новый элемент, вторая отменять последнюю операцию, а третья повторять отмененную операцию. Для того чтобы класс UndoManager смог работать с нашей операцией, необходимо описать ее с помощью интерфейса UndoableEdit. Он довольно громоздкий, поэтому проще воспользоваться абстрактным классом AbstractUndoableEdit,
Круговорот данных |
591 |
в котором уже многое реализовано, что мы и делаем. В конструктор операции следует передать стандартную модель списка и новый элемент. В общем же требуется лишь описать методы undo() и redo(), в которых мы удаляем новый элемент или заново добавляем его. Все довольно очевидно, есть лишь одна деталь — необходимо вызвать метод базового класса, так как он должен знать, когда мы отменили или повторили операцию, чтобы затем класс UndoManager смог узнать, доступна ли в данный момент отмена или повтор.
Когда изменение данных описано в требуемом виде, остается регистрировать его
вобъекте UndoManager в момент изменения, что мы и делаем методом addEdit(). Оставшееся является делом техники — кнопки отмены и повтора операций просто вызывают методы undo() и redo() для объекта UndoManager, предварительно удостоверившись
втом, что это возможно, методами canUndo() и canRedo(). Запустив пример, вы убедитесь
втом, что отмена и повтор операций работает на обоих списках, и при этом стандартная модель сама оповещает их об изменениях, а непосредственно изменения работают только с данными, ничего не зная о том, какие графические компоненты и где их отображают.
Это лишь начальный пример, и отталкиваясь от данных и моделей, мы сможем легко встроить в приложение отмену и повтор любых операций, причем на гибкости приложения и легкости изменения его графического интерфейса это никак не должно отразиться. Отделение данных от их отображения и деталей прорисовки на экране всегда платит сторицей в виде надежных и практичных приложений.
Резюме
Хотя обмен данными с другими приложениями системы или внутри единственного приложения, а также поддержка отмены случайных изменений в этих данных уже косвенно относятся к Swing и графическим компонентам, библиотека предоставляет весь арсенал, необходимый для реализации этих возможностей. Если в случае обмена данными за нас многое сделано и встроено на самом базовом уровне компонента JComponent, то при отмене и повторе изменений у нас есть лишь легкий фундамент UndoManager, который, тем не менее, является хорошей отправной точкой.