Основные концепции |
25 |
вающий предназначение вашего приложения (конечно, это довольно долго и недешево, но всегда можно использовать внешний вид от стороннего производителя). Никто не запрещает вам создать абсолютно новаторский трехмерный интерфейс или интерфейс, основанный только на системе синтеза и распознавания речи. Причем вам не нужно будет изменять ни строчки кода в вашем приложении.
Специальные средства для пользователей с ограниченными возможностями
Практически в любой современной системе создания пользовательского интерфейса поддерживаются специальные средства для работы людей, которые по каким то причинам (травмы или болезни) не способны работать с компьютером обычным образом (они могут не видеть экран, им может быть неудобно манипулировать мышью и клавиатурой). Зачастую единственным, что связывает таких людей с окружающим миром, становится компьютер. Поэтому очень важно обеспечить приложения механизмом, позволяющим специальными средствами передавать информацию пользователям в нужном виде.
Надо сказать, что, создавая приложение с помощью Swing, вы уже выполняете большую часть работы, необходимой для поддержки специальных средств. Разработчики Swing учли важность этой поддержки и встроили во все компоненты особую информацию. Эта информация (она может быть передана специальным средствам, которые обработают ее надлежащим образом) описана во внутренних классах компонентов, использующих библиотеку Accessibility (эта библиотека относится к набору Java Foundation Classes).
Итак, в каждом компоненте библиотеки Swing имеется внутренний класс, имя которого составляется из названия класса компонента и слова «Accessible», например, в классе кнопки (JButton) имеется внутренний класс AccessibleJButton. В классах AccessibleXXX содержится исчерпывающая информация о компонентах Swing (набор «ролей», которые исполняют графические компоненты в пользовательском интерфейсе; действия, которые можно совершать над компонентом, основные свойства компонента, например, текст надписи). Если на компьютере установлено специальное средство, оно находит эту информацию (вызывая для этого метод getAccessibleContext(), встроенный в общий класс всех компонентов Swing JComponent) и выводит ее в надлежащем виде (например, для человека с расстройством зрения интерфейс будет «прочитан» путем синтеза речи). Практически все известные средства для «чтения» интерфейсов или работы через специальные устройства поддерживают библиотеку Accessibility, к примеру, известная программа JAWS.
Вся работа фактически уже сделана разработчиками Swing: например, если вы создаете кнопку с надписью (не предпринимая никаких дополнительных усилий), специальное средство сразу получит информацию о том, что в пользовательском интерфейсе программы имеется кнопка, название кнопки и данные о том, что кнопку можно «нажать». Что делать с этой информацией дальше, зависит от ситуации и от того, как именно пользователю удобно получать информацию. Самое главное здесь — это то, что вы просто пишите приложение, совершенно не задумываясь о том, что в дальнейшем оно может быть использовано человеком с ограниченными возможностями, и, тем не менее, вся необходимая информация будет на месте. Учитывая другие достоинства Swing: независимость от конкретной платформы, способность динамически менять внешний вид и поведение (что особенно важно для специальных средств, при использовании которых возможно придется увеличивать размеры компонентов, по-особому обрабатывать действия, озвучивать их), простую локализацию и поддержку Unicode, можно сказать, что для специальных средств библиотека Swing подходит как нельзя лучше.
26 |
ГЛАВА 1 |
Если вам вдруг понадобиться проверить, как Swing справляется с поддержкой специальных средств, это всегда можно сделать. Загляните на официальный сайт java.sun.com, там вы найдете несколько инструментов для тестирования, а также ссылки на реальные специальные средства с поддержкой библиотеки Accessibility.
Локализация приложений
В современном мире, который уже практически полностью сдался на милость глобализации и информатизации, возможность быстро и без особых усилий подготовить приложение для работы на других языках и в других условиях невероятно ценна. В об- щем-то, разрабатывая приложение для более или менее широкого круга пользователей, особенно если оно разрабатывается в большой корпорации, можно сразу же начинать готовиться к тому, что его нужно будет выпускать сразу на нескольких языках.
Библиотека Swing великолепно справляет с задачей представления приложений на нескольких языках, во многом потому, что сам язык Java создавался как язык, способный корректно работать в таких ситуациях. Вынос всего текста, используемого в приложении, во внешние файлы .properties, поддержка этими файлами всего спектра символов Unicode, а значит всех мировых языков, хранение всех строк в самой программе как Unicode, позволяют безболезненно переводить программу на любые языки. Поддержка часовых поясов, дат, времени, чисел всех мировых культур также встроена в Java и доступна в Swing. Легко изменяемый внешний вид, реакция на действия пользователей, доступная нам благодаря подключаемому внешнему виду и поведению, позволит адаптировать или даже «замаскировать» приложение, так что его легко можно будет принять за другое, или написанное специально для определенных компаний или условий.
Все это еще раз доказывает, что удачная модель библиотеки Swing, вкупе с возможностями самого языка Java, позволяет создавать приложения самой сложной структуры и рассчитывать на их легкую поддержку и широкую масштабируемость.
Резюме
Итак, мы узнали, что основой Swing является библиотека AWT. Тем не менее, возможности библиотеки Swing гораздо богаче, к тому же она по праву «носит звание» библиотеки, полностью написанной на Java. Благодаря технологии JavaBeans в визуальных средствах разработки программ удалось упростить использование компонентов Swing и классов, поля и методы которых формируются по простым правилам. Подключаемые внешний вид и поведение компонентов, а также встроенная поддержка специальных возможностей придают Swing необычайную гибкость и избавляют нас от многих проблем и рутинной работы.
Глава 2. Модель событий
Графический пользовательский интерфейс (GUI) относится к системам, управляемым по событиям (event-driven systems). При запуске программы вы создаете пользовательский интерфейс, а затем ждете наступления некоторого события: нажатия клавиши, движения мыши или изменения компонента системы. При наступлении события программа выполняет необходимые действия, а затем снова переходит к ожиданию. Программа, использующая для создания пользовательского интерфейса библиотеку Swing, не является исключением. В этой главе мы увидим, как обрабатывать события
вSwing, и рассмотрим основные типы событий, общие для всех графических компонентов.
Для любой библиотеки пользовательского интерфейса очень важно качество используемой в ней системы обработки событий. Как бы библиотека ни была хороша внешне или функционально, неудачно реализованная обработка событий сведет все ее преимущества «на нет». Библиотека AWT из первого выпуска JDK — хороший пример. Несмотря на то, что она не блистала качеством и внешним видом компонентов, основным нападкам подвергалась именно неудачная система обработки событий, и, будь она получше, возможно, у AWT было бы более светлое будущее. Однако система обработки событий AWT была не слишком приспособлена к созданию масштабных программ — код, обрабатывающий события, находился прямо в классах компонентов: приходилось наследовать от них, искать нужное событие, пользуясь уже набившими оскомину операторами if и switch, и смешивать пользовательский интерфейс с деловой логикой программы. В итоге получалась программа, которую иногда было тяжело поддерживать и обновлять. Все это привело к тому что библиотека AWT была принята не слишком-то тепло.
Ксчастью, нам не придется возвращаться во времена старой системы обработки событий, и вспомнили мы ее лишь из уважения к истории развития библиотек пользовательского интерфейса в Java. Библиотека Swing использует систему обработки событий JavaBeans, и система эта действительно хороша. С одной стороны, она проста и понятна, с другой, предоставляет множество способов обработки событий, и вам остается лишь выбрать из них наиболее подходящий. Основным достоинством системы обработки событий Swing следует признать то, что как бы вы ни писали свою программу, какие бы способы создания интерфейса ни применяли, код, отвечающий за создание интерфейса, будет отделен от кода, обрабатывающего события. Это позволит и легко обновлять программу, и легко понимать ее.
Прежде чем перейти к описанию системы обработки событий Swing, мы узнаем, на базе какого решения она была создана. Во времена прежней системы существовало мнение, что хорошая система обработки событий в Java не будет создана до тех пор, пока
вязыке не появятся указатели на методы, что позволило бы использовать технику обратных вызовов (callbacks). Эта техника довольно характерна для обработки событий — вы передаете графическому компоненту ссылку на метод, который будет обрабатывать событие, и при возникновении этого события компонент вызывает ваш метод по полученной ссылке. Элегантное решение было найдено, когда создатели новой системы обработки событий решили использовать опыт объектно-ориентированного программирования, обратившись к шаблонам проектирования.
28 |
ГЛАВА 2 |
Наблюдатели
В программировании довольно часто возникает ситуация, когда при изменении данных в одном месте программы необходимо тут же обновить данные в другом месте программы. Если мы используем объектно-ориентированный язык программирования, это означает, что при изменении состояния одного объекта необходимо каким-либо образом изменить другой объект. Самое простое решение — организация непосредственной связи между объектами, то есть поддержание ситуации, когда объекты хорошо осведомлены о существовании друг друга. Первый объект знает, что изменение его состояния интересует другой объект, у него есть ссылка на этот объект, и каждый раз при изменении своего состояния он сообщает об этом другому объекту. В такой ситуации второй объект (получающий сообщения об изменениях) чаще всего нуждается в дополнительной информации о первом объекте (в котором происходят изменения), поэтому у него есть ссылка на первый объект. В результате получается то, что мы называем сильно связанными объектами — два объекта хранят ссылки друг на друга.
Однако это простое решение оказывается весьма ограниченным. Любое последующее изменение в программе приведет к тому, что придется основательно ее переписывать. Если изменениями в первом объекте интересуется какой-то объект, вполне возможно, что в будущем этими изменениями заинтересуется еще один объект, а потом еще несколько. Единственным способом обеспечить их информацией об изменениях в объекте станет переписывание исходного кода программы, чтобы включить в него ссылки на новые объекты. Однако делать это каждый раз при изменении количества заинтересованных объектов неудобно, долго и чревато ошибками. А если до запуска программы количество заинтересованных объектов вообще неизвестно, то наше простое решение совсем не подойдет.
Описанная ситуация встречается довольно часто, так что неудивительно, что наилучший вариант решения уже был найден в форме наблюдателя — одного из самых известных шаблонов объектно-ориентированного проектирования. Наблюдатель определяет, как следует организовать взаимоотношения между объектами, чтобы избежать сильной связи между ними и таким образом добиться необходимой гибкости. Давайте посмотрим, что этот шаблон из себя представляет.
Итак, объект, за которым ведется наблюдение, называется субъектом (subject). Объект, заинтересованный в изменениях субъекта, называются наблюдателем (observer). В обязанности субъекта входит добавление наблюдателей (в идеале количество наблюдателей произвольно), отправка наблюдателям сообщений об изменениях своего состояния и отсоединение ранее добавленных наблюдателей. Наблюдатели проще субъектов: они определяют методы, которые следует вызывать субъекту, для того чтобы сообщить о своих изменениях (рис. 2.1).
Субъект (subject) add() remove() notify()
notify() вызывает метод update()
Наблюдатель |
Наблюдатель |
Наблюдатель |
(observer) |
(observer) |
(observer) |
update() |
update() |
update() |
Рис. 2.1. Взаимоотношения наблюдателей и субъектов
Модель событий |
29 |
Рисунок показывает, что субъект обладает тремя методами: метод add() позволяет добавить очередного наблюдателя (как правило, наблюдатели хранятся в виде списка, что позволяет иметь произвольное их количество и легко манипулировать ими); метод remove() позволяет удалить ранее добавленного наблюдателя; метод notify() сообщает наблюдателям, добавленным ранее методом add(), о смене состояния субъекта. Для этого он вызывает определенный во всех объектах-наблюдателях специальный метод, в нашем случае это метод update(). В свою очередь наблюдатели знают, что очередной вызов метода update() означает смену состояния субъекта, и выполняют в этом методе все необходимые действия. Чтобы субъекты и наблюдатели смогли работать друг с другом, их функции описываются в базовых классах (или интерфейсах), и объекты, которым необходимо знать друг
одруге, но которым нежелательно быть сильно связанными, наследуют от этих базовых классов или реализуют интерфейсы и начинают работать как субъекты и наблюдатели.
Ничего сложного в субъектах и наблюдателях нет, тем не менее, они позволяют объектам просто и чрезвычайно элегантно узнавать друг о друге. Объекты, изменения в которых могут быть интересны окружающим, становятся субъектами (то есть просто поддерживают список наблюдателей), а заинтересованным в их изменениях объектам нужно лишь определить методы наблюдателей и зарегистрировать свой интерес к субъектам. Никто не запрещает субъектам самим быть наблюдателями, и наоборот. В Java субъекты и наблюдатели могут работать особенно эффективно — возможность определять внутренние классы позволяет одному классу одновременно иметь информацию
онескольких совершенно разных субъектах и эффективно обрабатывать ее.
Нетрудно заметить, что шаблон наблюдателя должен прекрасно работать в системах обработки событий, происходящих в пользовательском интерфейсе. Действительно, субъектами являются различные графические компоненты, из которых состоит интерфейс — кнопки, списки, текстовые поля, а программист определяет, что в них происходит (щелчки на кнопках, перемещения мыши), описывает объекты-наблюдатели и решает, как поступить при смене состояния интересующего его компонента. По сути, все действует по законам знакомой нам по первой главе архитектуры «модель-вид-контроллер», только на более высоком уровне. Компонент представляет собой вид, в модели хранятся данные, так, как это удобно программе, ну а наблюдатель является контроллером, только уже более высокого уровня, который определяет что будет происходить в программе в ответ на происходящие события, то есть контроллером всей программы или ее части.
Интересно, что вся библиотека Swing буквально «напичкана» субъектами и наблюдателями. Мало того, что эта концепция используется при обработке событий, она еще позволяет моделям и UI-представителям Swing иметь самую свежую информацию друг о друге. И модели, и UI-представители одновременно представляют собой субъектов и наблюдателей — при изменении данных модели она уведомляет об этом UI-представи- теля (выступающего в качестве наблюдателя), и тот обновляет внешний вид компонента в соответствии с новыми данными. Если же в ответ на действие пользователя меняется внешний вид компонента (а за это отвечает UI-представитель), то уже модель становится наблюдателем и получает уведомление о том, что данные необходимо изменить.
Но вернемся к системе обработки событий Swing. Она на самом деле основана на отношении вида субъект-наблюдатель. Субъектами являются компоненты Swing (кнопки JButton, списки JList и т. п.), а наблюдателями — специальные объекты, которые называют слушателями. Для того чтобы узнать о каком-либо событии, надо написать соответствующего слушателя и присоединить его к компоненту.
Слушатели
Событие (event) в пользовательском интерфейсе — это либо непосредственное действие пользователя (щелчок или движение мыши, нажатие клавиши), либо изменение состояния какого-либо компонента интерфейса (например, щелчок мыши может при-