Многие Swing-компоненты имеют связанные с ними интерфейсы (Model), которые отвечают за доступ к данным и генерацию событий, связанных с изменением данных.
Архитектура MVC (модель – вид – контроллер) – это объектно-ориентированная архитектура пользовательских интерфейсов, состоящая из трех частей.
Модель предназначена для хранения, изменения и получения данных графического компонента.
Вид представляет данные на экране.
Контроллер обеспечивает реакцию модели и вида в ответ на действия пользователя.
Архитектура MVC реализована в библиотеке Swing в виде архитектуры с разделенной моделью, состоящей из двух, а не из трех частей.
Вид и контроллер, в архитектуре Swing, объединены вместе в один элемент – представителя пользовательского интерфейса (delegate UI).
Класс графического компонента связывает модель и представителя с помощью менеджера пользовательского интерфейса (UIManager), который определяет внешний вид и поведение интерфейса (Look and Feel).
Именно поэтому библиотека Swing имеет подключаемую архитектуру look-and-feel.
Таким образом, для реализации модели MVC библиотека Swing использует делегирование (delegation) полномочий, назначая в качестве модели данных представителя (delegate) – экземпляр класса с именем вида xxxModel.
Класс библиотеки Swing содержит защищенное или даже закрытое поле model – объект этого класса-модели, и метод getModel, предоставляющий разработчику доступ к полю model.
Для обеспечения внешнего вида и поведения Swing компонента также используется делегирование полномочий.
Swing класс JComponent содержит защищенное поле ui – экземпляр класса-представителя ComponentUI из пакета javax.swing.plaf, непосредственно отвечающего за вывод изображения на экран в нужном виде.
Класс-представитель содержит методы paint и update, формирующие и обновляющие графику компонента.
Такие представители образуют целую иерархию с общим суперклассом ComponentUI.
Они собраны в пакет javax.swing.plaf и его подпакеты.
В их именах есть буквы UI (User Interface), например, ButtonUI.
Представители класса тоже являются полями класса Swing компонента, и доступ к ним осуществляется методами вида getUI.
При построении графического интерфейса пользователя редко приходится обращаться к моделям и представителям компонента.
В большинстве случаев достаточно обращаться к методам самого класса компонента.
Если есть желание поменять модель, принятую по умолчанию, можно заменить ее другой моделью, реализовав подходящий интерфейс или расширив существующий класс xxxModel.
Новая модель данных устанавливается методом setModel (xxxModel).
Для создания графического интерфейса пользователя с использованием библиотеки Swing в среде разработки IntelliJ IDEA, нужно нажать правой кнопкой мышки на пакете приложения и выбрать New – GUI Form.
Ввести имя класса и нажать OK.
В результате будет создан Java класс и его XML описание с расширением. form, которое открывается в визуальном редакторе, позволяя визуально создавать и редактировать GUI интерфейс.
Первоначально форма состоит только из контейнера JPanel.
Для дальнейшей работы с GUI интерфейсом, нужно в поле field name ввести имя этого компонента.
При желании можно изменить компоновку компонента.
Далее нужно открыть связанный с формой Java класс и в коде класса нажать правой кнопкой мыши и выбрать Generate – Form main ().
В результате будет сгенерирован код запуска формы.
А именно, будет сгенерирован метод main, в котором будет создано окно верхнего уровня JFrame.
И в это окно будет добавлена панель формы.
Теперь можно вернуться в визуальный редактор и добавлять компоненты в форму.
При этом по умолчанию в Java класс будут добавляться только соответствующие поля.
Весь остальной код будет генерироваться сразу в байт-код при компиляции на основании XML файла описания формы.
Изменить это можно в настройках File | Settings | Editor | GUI Designer, выбрав опцию Java source code.
При этом при компиляции будет генерироваться исходный код, который менять нельзя.
Для добавления слушателя к компоненту, нужно нажать правой кнопкой мыши на компоненте и выбрать Create Listener.
В результате в конструкторе класса будет сгенерирован слушатель действий компонента, в который можно добавлять нужный код.
Если у вас есть поля в Swing компоненте, которые вы хотите связать с данными класса JavaBean, нужно нажать правой кнопкой мыши на компоненте и выбрать Data Binding Wizard.
Дальше следовать подсказкам.
В результате будет сгенерирован Plain Old Java Object класс.
И будет сгенерирован код, связывающий Plain класс с GUI компонентом.
В GUI компонент можно добавить пользовательское свойство, которое отобразится в визуальном редакторе.
Для этого нужно нажать на поле Client Properties.
И открыть окно свойств.
В левой панели выберите класс, для которого вы хотите изменить свойства.
С выбранным классом в левой панели нажмите кнопку + на правой панели.
И добавьте свойство.
JButton и JLabel
Палитра компонентов позволяет добавить кнопку JButton.
Для кнопки можно изменить цвет фона, шрифт, цвет надписи, выравнивание надписи, саму надпись, текст подсказки при наведении курсора мыши на кнопку, добавить слушателя действий пользователя.
Также можно добавить значок в кнопку.
Компонент JLabel может отображать либо текст, либо изображение, либо и то, и другое.
Содержимое метки может выравниваться, с помощью установки вертикального и горизонтального выравнивания в области отображения.
Компонент JLabel используется для отображения одной строки только для чтения.
Текст может быть изменен приложением, но пользователь не может его редактировать напрямую.
С помощью разметки HTML можно создавать многострочную метку, комбинируя шрифты и цвета.
JColorChooser
Компонент JColorChooser используется для создания диалогового окна выбора цвета.
Использовать компонент JColorChooser очень просто.
Вызываем статический метод showDialog, который отображает модальное диалоговое окно выбора цвета.
Это диалоговое окно блокирует приложение до тех пор, пока оно не будет закрыто.
В метод showDialog передается цвет выбора по умолчанию.
Пользователь может в диалоге изменить этот цвет выбора.
После закрытия диалогового окна метод showDialog возвращает цвет выбора, который далее может быть использован в приложении.
JCheckBox, JRadioButton, JToogleButton
Компонент JCheckBox используется для создания флажка.
Щелчок мыши на флажке изменяет его состояние с «on» на «off» и наоборот.
Методом setMnemonic можно определить горячие клавиши для этой кнопки.
А флажок является кнопкой, так как наследует от класса AbstractButton.
Компонент JRadioButton используется для создания переключателя.
Он используется для выбора только одной опции из нескольких вариантов.
Первоначальный выбор радио кнопки указывается в конструкторе класса, с помощью значения аргумента true.
Для того чтобы выбор кнопки был единственным, кнопки нужно объединить в группу ButtonGroup.
Если это не сделать, выбор будет множественным.
Компонент JToggleButton реализует функции переключения, которые наследуются компонентами JCheckBox и JRadioButton.
Компонент JToggleButton используется для создания кнопок с двумя состояниями – включен и выключен.
Компонент JToggleButton создается с помощью конструктора класса, в котором можно указать надпись кнопки, иконку и состояние.
Проверить нажата ли кнопка, можно с помощью метода isSelected.
JComboBox
Компонент JComboBox объединяет кнопку или редактируемое поле и раскрывающийся список.
По умолчанию компонент представляет собой не редактируемое поле, в котором есть кнопка и раскрывающийся список значений.
Создать экземпляр раскрывающегося списка можно конструктором по умолчанию JComboBox, а затем вносить в него элементы методами addItem (Object) и insertItemAt (Object, int).
Однако удобнее предварительно создать массив или вектор, содержащий элементы, и внести его в список сразу же при его создании конструктором JComboBox (Object []) или JComboBox (Vector).
Получить элемент списка, который выбрал пользователь, можно с помощью метода getSelectedIndex или getSelectedItem.
Вместо строк, список можно составить из изображений, или других объектов.
Список становится редактируемым с помощью вызова метода setEditable (true).
Здесь показан пример, в котором пользователь может отредактировать выбранное поле, и оно будет сохранено в списке.
Редактирование выбранного элемента списка не приводит к изменению этого элемента в списке, а влияет только на объект, возвращаемый методом getSelectedItem.
Поэтому здесь отредактированный элемент сохраняется в списке программным способом.
Здесь нужно учитывать, что слушатель ActionListener получает событие ActionEvent, когда был сделан выбор.
И если поле со списком доступно для редактирования, тогда событие ActionEvent также будет сгенерировано, когда закончится редактирование.
Таким образом, обработчик ActionListener вызывается два раза.
Поэтому сначала мы запоминаем индекс выбранного элемента, а затем при получении команды редактирования, сохраняем новое значение по указанному индексу.
Для изображения элементов списка используется объект, реализующий интерфейс ListCellRenderer.
Этот объект последовательно выводит элементы, переходя от одного элемента к другому.
С помощью такого объекта устраняется необходимость создания своего графического объекта для каждого элемента списка, что значительно экономит ресурсы.
Метод getListCellRendererComponent интерфейса ListCellRenderer отвечает за формирование компонента и размещения в нем текущего элемента списка value, имеющего порядковый номер index.
Полученный таким образом компонент затем выводится на экран своим методом paint.
В библиотеке Swing интерфейс ListCellRenderer реализован классами BasicComboBoxRenderer и DefaultListCellRenderer, расширяющими класс JLabel.
Именно потому, что выводом элементов фактически занимается класс JLabel, можно использовать в элементах списка текст или изображение.
Здесь показана пользовательская реализация интерфейса ListCellRenderer, которая выводит флажки в качестве элементов списка.
JScrollPane
Компонент JScrollPane является контейнером, который может содержать только один компонент и используется для создания прокручиваемого представления компонента.
Когда размер экрана ограничен, используется панель прокрутки для отображения большого компонента или компонента, размер которого может изменяться динамически.
Полосы прокрутки могут всегда отображаться на экране, отображаться при необходимости или не отображаться вообще. Это определяется методами setVerticalScrollBarPolicy и setHorizontalScrollBarPolicy.
На самом деле кроме своего содержимого и двух полос прокрутки эта панель может содержать еще шесть компонентов: заголовок, столбец слева, и четыре компонента по углам.
Эти компоненты устанавливаются методами setColumnHeaderView, setRowHeaderView и setCorner соответственно.
Компонент помещается на панель прокрутки сразу же при ее создании конструктором класса JScrollPane или позднее методом setViewportView.
На самом деле компонент отображается в панели прокрутки в окне содержимого JViewport, которое содержит панель JScrollPane.
Это окно можно получить методом getViewport класса JScrollPane, а затем можно добавить в него компонент обычным методом add.
JList
Компонент JList отображает список объектов и позволяет пользователю выбрать один или несколько элементов в списке.
Список можно создать с помощью конструктора по умолчанию JList, создающего пустой список.
Можно создать список с заданным массивом объектов конструктором JList (Object []), или с заданным вектором при помощи конструктора JList (Vector) или с определенной заранее моделью JList (ListModel).
Это делается так же, как и при создании выпадающего списка JComboBox.
Чтобы ограничить число видимых на экране строк, но при этом отобразить весь список, следует поместить список в панель JScrollPane.
При этом метод setVisibleRowCount задает число видимых на экране строк.
Значение по умолчанию равно 8.
Вызов этого метода с отрицательным значением эквивалентно значению нуль.
Метод setLayoutOrientation определяет компоновку элементов списка.
Константа VERTICAL определяет расположение элементов вертикально в одном столбце.
Константа HORIZONTAL_WRAP определяет расположение элементов горизонтально, при необходимости с переносом их на новую строку.
Константа VERTICAL_WRAP определяет расположение элементов по вертикали, при необходимости с переносом их в новый столбец.
В сочетании с вызовом метода setLayoutOrientation, вызов метода setVisibleRowCount (-1) делает список отображающим максимальное количество элементов в доступном пространстве на экране.
Методами setFixedCellHeight и setFixedCellWidth можно установить высоту и ширину отображаемых строк списка.
Так же, как и в раскрывающийся список JComboBox, в список JList можно добавлять не только текст, но и изображения.
По умолчанию в списке можно выбрать любое число любых элементов, держа нажатой клавишу
После применения метода setSelectionMode с константой SINGLE_SELECTION, в списке можно будет выбрать только один элемент.
Также, как и для раскрывающегося списка JComboBox, для списка JList, можно определить пользовательский объект ListCellRenderer, и отображать в списке любые компоненты, а не только строки и изображения.
Список генерирует события выбора списка при каждом изменении выбора.
Вы можете обработать эти события, добавив в список слушатель с помощью метода addListSelectionListener.
Слушатель выбора списка должен реализовать один метод valueChanged.
Метод getValueIsAdjusting возвращает true, если пользователь все еще выбирает в списке.
Так как нам нужен только конечный результат действия пользователя, метод valueChanged делает что-либо, только если getValueIsAdjusting возвращает false.
В этом примере описан случай, когда список допускает множественный выбор, поэтому метод valueChanged обрабатывает массив индексов выбранных пользователем элементов списка.
Архитектура Model-View-Controller
Теперь, когда мы уже рассмотрели ряд Swing компонентов, давайте вернемся к архитектуре model-view-controller (MVC).
MVC – это общий подход к построению графических приложений.
Суть идеи заключается в том, чтобы низлежащие данные, отображение и логика, которая контролирует данные и отображение, должны быть развязаны.
Эта идея исходит из того, что мы хотели бы иметь более одного способа посмотреть на одни и те же данные.
Например, в большинстве приложений, разработанных для финансовых компаний, существуют разные экраны, которые позволяют просматривать один и тот же торговый процесс по-разному.
Еще один пример из реляционных баз данных.
Там мы можем запускать разные запросы для выбора и упорядочивание данных, но низлежащие данные всегда одинаковы.
Поэтому архитектура MVC предлагает нам строить графические интерфейсы с помощью моделей, видов и контроллеров.
Модель является источником данных, а вид – это Представление данных.
Модель ничего не знает вообще о Представлениях.
Однако, если Модель является динамичной, тогда она обеспечивает интерфейс прослушивания.
Таким образом, Представление является просто наблюдателем модели.
Когда данные в Модели меняются, она генерирует событие, отражающее изменение.
Все Представления, которые слушают эту Модель в качестве наблюдателей, получают обновление и перерисовывают сами себя.
Хорошо, где Контроллер в этой картине?
Контролер отвечает за изменение модели.
С помощью Контроллера, мы можем изменить то, как компонент отвечает на запросы пользователя без изменения его визуального представления.
Обычно Контроллер состоит из графической части и некоторой логики приложения.
Например, вы хотите добавить меню в свой редактор.
Предположим, перед этим, ваш контроллер захватывал события нажатия определенных комбинаций клавиш клавиатуры и выполнял соответствующие действия на их основе.
Теперь, добавляя меню, вы делаете его частью своего контроллера.
Когда пользователь выбирает элемент меню он действует так, как если бы была бы нажата определенная комбинация клавиш.
Контроллер – это способ, которым пользователь меняет модель.
Контроллер не обновляет Представление, потому что оно автоматически получает обновления, как наблюдатель модели.
Давайте теперь посмотрим на пример MVC.
Рассмотрим модель SimpleStringModel, которая будет иметь один контроллер и несколько видов.
В этой модели у нас есть два метода getString и setString.
В методе setString мы устанавливаем новое значение поля класса и уведомляем всех слушателей модели, вызывая метод интерфейса, который эти слушатели реализуют.
Соответственно объект модели хранит список своих слушателей.
Представление здесь – это компонент, расширяющий метку.
Представление имеет метод setModel, в котором Представление становится слушателем Модели.
При изменении модели, автоматически вызывается метод setText метки, который изменяет надпись метки.
Контроллер здесь текстовое поле, в которое пользователь вводит строку текста, и эта строка становится новым значением Модели.
Этот базовый пример иллюстрирует, как реализуется архитектура MVC.
Еще одна интересная и очень полезная функция, которую мы получаем, когда используем MVC.
Предположим, что допустимы не все значения, которые пользователь может ввести в текстовое поле.
Путем выброса исключения в методе set модели мы можем запретить изменение представления.
Swing реализация MVC объединяет Controller и View представление.
На самом деле это не очень сложно сделать.
Вы просто размещаете функции View представления и Сontroller в одном классе.
В предыдущем примере у нас было два графических компонента – один для Представления, а второй для Контоллера.
Здесь у нас один графический компонент, который при взаимодействии с пользователем меняет модель.
При этом этот же компонент становится слушателем модели, перерисовывая себя при изменении модели.
Давайте посмотрим, как реализована архитектура MVC в Swing на примере списка.
Давайте посмотрим на интерфейс ListModel, представляющий модель данных списка.
Во-первых, этот интерфейс легковесный, так как в нем нет ссылки на сам список.