Swing для среднего уровняИсточник: IBM developerWorks Россия Майкл Абернети
О данном руководствеДанное руководство предназначено для тех из вас, кто имеет опыт работы со Swing-приложениями, но хотел бы расширить знания и использовать приемы повышенной сложности - вещи, которые возможно нельзя сразу осознать, просто посмотрев на Swing API. Решив прочесть данное руководство, вы должны быть знакомы с основными концепциями Swing, такими как UI-виджеты Swing, схемы, события и модели данных. Если вы все же думаете, что вам надо освежить эти концепции, обратитесь к руководству "Введение в Swing", в котором охвачены все эти темы, а также дается необходимая информация для работы с данным руководством. Во время работы с данным руководством вы познакомитесь с аспектами Swing, выходящими за рамки основных компонентов и приложений. Эти области знания являются более трудными для изучения и освоения, но также являются и более мощными, позволяющими вам создавать более совершенные приложения. В данном руководстве рассматриваются следующие концепции Swing повышенной сложности:
Загрузка инструментальных средств и исходного кодаДля работы с данным руководством вам понадобится следующее:
Пример приложенияНапоминаниеПеред переходом к новым концепциям давайте вернемся к примеру приложения, начатому вами в предыдущем руководстве - системе резервирования билетов. Для тех, кто пропустил первое руководство, данное краткое изложение должно помочь быстро понять, что это за приложение. Если вы помните, приложение пытается смоделировать систему резервирования билетов, которая располагается на вашем рабочем столе. Оно позволяет пользователям выбирать город отправления и город прибытия, а затем выполнить поиск рейсов, соответствующих критерию поиска. Эти рейсы отображаются в табличной форме, и пользователь может выбрать желаемый рейс и купить любое количество билетов. На данный момент приложение очень простое; только взглянув на него, вы должны понять его роль, а также текущие ограничения, которые мы в данном руководстве будем устранять.
Ограничения приложенияВзглянув на существующее приложение, вы, возможно, полагаете, что это все, что нам нужно для работы. Возможно это так, но существует еще один взгляд - приложение имеет все необходимое для работы, но не имеет всего необходимого для хорошей работы. Именно это отличает плохие приложения от хороших - более совершенные детали, которые делают UI гармоничнее. Хотя приложение в текущем состоянии будет работать как система резервирования билетов, кто из вас, читающих сейчас это руководство, хотел бы видеть нечто подобное на вашем рабочем столе? Наверное, не многие. Оно выглядит элементарным, отсутствует изысканность, и оно вообще не очень дружественно пользователю. Вот это и есть главное предназначение данного руководства: улучшение впечатления пользователя Sing-приложения, и превращение функционального приложении в приложение, имеющее товарный вид. Вы узнаете, как улучшить ваше приложение, так чтобы оно выглядело и вело себя как профессиональное приложение. Swing предлагает инструментальные средства для того, чтобы сделать это с намного меньшими усилиями, чем вы могли бы представить. Существует также множество приложений сторонних производителей (некоторые из них я рассмотрю в данном руководстве), заполняющих пробелы, которые присутствуют в Swing. Улучшения примера приложенияЧтобы придать базовому приложению профессиональный вид, в данном руководстве мы сфокусируемся на следующих моментах:
JTable в деталяхВведениеЕсли вы когда-либо работали с JTable, то, несомненно, сталкивались с некоторыми из трудных концепций, которые представляет JTable. Возможно, самым общим недовольством UI-разработчиков является то, что базовая версия является слишком простой и не имеет применения в реальных приложениях. Однако более сложные JTable, которые становятся полезными, добавляют много уровней сложности, с которыми хотят иметь дело не все разработчики. Да, у них нет выбора. Во-первых, давайте подумаем о том, чего не хватает в JTable резервирования билетов. С первого взгляда можно заметить, что выбранные для ячеек (или для выделения) цвета, возможно, не такие, как мы хотим. Или, возможно, вы не хотите отображения сетки в таблице. То есть, одной из вещей, которые можно изменить, является внешний вид . Как же все-таки насчет поведения ? Что еще не так с базовой JTable? Например, она не обеспечивает какого-либо механизма сортировки. Это большой пробел - JTable предоставляет данные пользователю, а потому она должна позволить пользователю решать, как разместить данные на экране. Как насчет некоторых других маленьких фишек? Например, выравнивание текста? Чаще всего, привычным является выравнивание текста по левому краю, а чисел - по правому. Как насчет редактирования содержимого ячейки? Некоторые пользователи ожидают именно такого типа поведения. Хорошо, хорошо, хватит нападать на JTable. Вместо разговоров об отсутствующих функциях в простом объекте JTable покажите мне, как использовать его потенциал и превратить простой JTable в системе резервирования билетов в более приятную таблицу, которую пользователи могут настраивать. Свойства JTableПростейший способ начать изменение объекта JTable - начать изменять его свойства . Класс
Как видите, без приложения значительных усилий можно сделать с объектом JTable достаточно много. Эти свойства позволяют настроить JTable в приложении под ваши собственные требования. Однако как бы ни были хороши эти маленькие свойства, они не дают ответа на некоторые более серьезные требования примера приложения. TableRendererВ предыдущем разделе вы видели, что можно изменить цвет переднего плана и фона ячейки при ее выборе и снятии выбора. Это прекрасная возможность, но она ограничена: Что если вы хотите менять также и шрифт? Что если вы хотите менять цвет в зависимости от конкретного значения этой ячейки? Такое более сложное рисование ячеек таблицы управляется интерфейсом, называемым В примере приложения мы изменим внешний вид таблицы, так чтобы он соответствовал следующим правилам:
Первым шагом к использованию нового
Теперь, после быстрого прохождения этого шага, давайте посмотрим на сам FlightTableRenderer . FlightTableRenderer использует преимущества встроенного Swing-класса DefaultCellRenderer , который предоставляет хорошую отправную точку. Мы можем расширять и переопределять единственный метод этого класса для выполнения нашего собственного окрашивания. Реальная логика окрашивания должна быть понятна без объяснений.
После вставки этого кода в пример приложения таблица результатов выглядит совершенно по-другому. Она также передает больше информации пользователю, поскольку затемняет рейсы, которые уже проданы:
TableModelВ руководстве для начинающих вы узнали, как использовать
СортировкаПользователи таблиц (независимо от того, являются ли они таблицами настольного или Web-приложения) ожидают наличия возможности сортировать данные в этих таблицах по столбцу. Другими словами, они хотят нажать на заголовке столбца и увидеть перегруппировку данных либо по возрастанию, дибо по убыванию, в зависимости от нажатого столбца. К сожалению, JTable не имеет такой функции, что действительно неприятно, поскольку почти каждому пользователю это нужно. Данная функциональность встроена в следующие версии Swing; но из-за большого количества пользователей, ожидающих ее сейчас, она уже предлагается в дополнении к JTable от сторонних производителей, которое можно довольно просто добавить в нашу систему резервирования рейсов. Иронией является то, что "приложение стороннего производителя", которое большинство пользователей использует для сортировки своих таблиц, предоставляется самой Sun в документации по JTable. Вызывает удивление, почему Sun просто не включила его в Swing автоматически вместо того, чтобы заставлять людей рыться в документации, не так ли? Предоставляемый фирмой Sun класс называется Мы можем добавить к системе резервирования билетов возможность сортировки всего лишь двумя строками кода:
На приведенном ниже рисунке показано, как выглядит сортировка в нашем приложении. Обратите внимание на стрелку после слова "Tixx". Она указывает на то, что столбец отсортирован, а также направление сортировки - по возрастанию или по убыванию:
Резюме по JTableИтак, всего лишь за несколько секунд при помощи некоторых новых классов мы преобразовали наш объект JTable из черствой и скучной таблицы в … нечто менее скучное (давайте не будем обманывать сами себя). В любом случае, мы многое добавили в наш объект JTable за несколько простых уроков, а вы сможете добавить еще больше, изучив эти уроки и основываясь на них. Вот краткий обзор рассмотренного нами материала:
На приведенном ниже рисунке показано, как сейчас выглядит таблица JTable в системе резервирования билетов:
Потокозащищенный SwingКогда UI застываетКогда я упоминаю слово threading (организация работы с несколькими потоками), мне кажется, что я слышу коллективный стон, идущий из толпы. Да, даже Swing не является неуязвимым для проблем многопоточности, и если вы похожи на меня, то это не то, что вы хотели бы услышать. Проблемы многопоточности часто являются головной болью при программировании - концепции абстрактны, а при возникновении ошибок их трудно протестировать и исправить. Концепции Swing, по крайней мере, намного легче понять и закодировать. Например, предположим (в нашей системе резервирования билетов), что пользователь нажал на кнопку для покупки билетов - в текущей версии приложения этот процесс завершается быстро, поскольку мы используем фиктивную базу данных. Но представим себе, что мы обращаемся к реальной базе данных, расположенной где-нибудь еще, как в классическом приложении клиент-сервер. Когда пользователь нажимает кнопку Purchase, мы должны вызвать и обновить базу данных и ожидать результат. Вы действительно собираетесь заставить пользователя ждать столько времени (возможно, до минуты), пока обработаете эту покупку? Надеюсь, что нет! Существует множество проблем, возникающих в Swing при кодировании подобного действия. Пользователь заметит сразу, что он (или она) заблокирован в приложении и не может взаимодействовать больше ни с чем; Swing выполняется в одном потоке (поток распределения событий - event-dispatch thread). Это означает, что поскольку поток сидит и ждет завершения вызова базы данных, любое другое пользовательское взаимодействие (например, попытка выбора другого города прибытия) помещается в очередь потока для завершения и, следовательно, ожидает завершения вызова базы данных. Мы не можем блокировать наших пользователей из их собственного приложения! Другой досадной особенностью Swing является то, что когда Swing-поток занят, а приложению нужно перерисовать себя (например, если вы минимизировали и затем максимизировали фрейм), команда repaint тоже блокируется вызовом базы данных, и пользователь видит серый прямоугольник. Эта проблема будет решена в следующей версии Swing, но до тех пор мы должны ее учитывать. Только эти две проблемы должны показать вам, насколько важным является корректная работа с многопоточностью в Swing - вероятно, вы не можете назвать свое приложение профессиональным, если вы блокируете ваших пользователей и превращаете приложение в серый прямоугольник каждый раз, когда приложению необходимо выполнить какой-либо код, занимающий больше, чем несколько миллисекунд. К счастью, есть решения этих общих проблем. Работа с Event-Dispatch ThreadВ Swing есть метод Первоначально мы запускаем нашу систему резервирования билетов следующим образом:
Однако мы должны воспользоваться возможностью улучшить производительность, предлагаемую методом
Однако это все равно не решает нашу первоначальную проблему: перемещение медленных процессов из EDT. Мы рассмотрим это далее. Решения Swing-многопоточности сторонних производителейПо иронии судьбы (вы уже с этим сталкивались), существует решение проблемы (я обсуждал ее в последних двух разделах), которое опубликовано в документации по Swing фирмы Sun, хотя, аналогично Класс называется Рассмотрим, что происходит, когда пользователь нажимает кнопку Search, как разбиваются части вызова и как решается, какие потоки они должны использовать для работы.
Класс
|
|
А вот кнопка с mouseover:
Дополнительные изменения в JCoolButton
Вы можете заметить, что JCoolButton пока еще не совершенен, поскольку градиентный фон все равно прорисовывается, когда курсор мыши не находится над ним, что выглядит громоздко. Мы должны добавить немного дополнительного кода (для того чтобы сделать JCoolButton полностью смешанной с фоном JPanel, когда над ней нет курсора мышки), совершенствуя прекрасный эффект кнопки.
Это важный урок по работе Swing-компонентов. Очень важно понимать, как компонент рисует себя на самом деле, в каком порядке и для каких ответвлений переопределяются индивидуальные функции. Этот урок начинается с функции paint()
в JComponent. Поскольку это JComponent, каждый Swing-компонент содержит ее, и она, фактически, является функцией, которая ответственна за рисование каждого единичного компонента в Swing.
В документации по paint()
указано, что при вызове метода он, в свою очередь, вызывает три других метода JComponent
: сначала paintComponent()
, затем paintBorder()
и, наконец, paintChildren()
. Из порядка вызова методов вы можете увидеть, что Swing рисует себя снизу вверх. Важным уроком является то, что для переопределения внешнего вида компонента вы должны переопределить метод paintComponent()
, который непосредственно несет ответственность за рисование конкретного компонента на экране.
Давайте исправим в нашем объекте JCoolButton
проблему, заключающуюся в прорисовке голубого градиентного фона даже при отсутствии курсора мышки над кнопкой. Вместо этого мы хотим смешать ее прямо с фоном окна. Мы можем сделать это путем переопределения метода paintComponent()
, отслеживания положения курсора мышки (когда он над, а когда нет) и прорисовки кнопки для каждого состояния. Одно важное замечание перед работой с исходным кодом: переопределив метод paintComponent()
, вы становитесь ответственны за все рисование. Другими словами, вы ответственны за текст, фон, декорации - в общем, за все, кроме границы.
|
А вот как она выглядит с mouseover:
Ваша творческая натура может быть стеснена существующими Swing-компонентами - не только их внешним видом, но и тем, как они работают. Вы можете решить, что нет существующих компонентов, которые могут делать то, что вы хотите. Вы имеете образ и хотите реализовать его в вашем UI.
Swing предоставляет вам возможность воплотить мечту в реальность, позволить разогнаться вашим творческим порывам и создать любой компонент с любой функциональностью, которую вы пожелаете иметь. И все это можно сделать всего за четыре шага:
Хорошо, достаточно абстрактных описаний. Давайте перейдем к примерам! Мы создадим новый компонент и выполним четыре шага, описанных в предыдущем разделе, чем покажем вам, как можно создать качественный новый компонент.
Компонентом, который будет создаваться, является JMenuButton, новый компонент, работающий как нормальный JButton, но с дополнительной функциональностью: пользователь может нажать кнопку со стрелкой с краю JButton для показа дополнительных вариантов. Если это описание запутанно (а я уверен, что да), подумайте о кнопке Back вашего Web-браузера: ее нажатие направляет вас назад к последней прочитанной вами Web-странице, но ее нажатие и удержание вызывает появления списка страниц, имеющихся в вашей истории посещений. Это, по существу, JMenuButton, и мы будем создавать одну из них в Swing. Вы увидите, что ничего похожего не существует в текущей версии Swing, но все строительные блоки имеются для создания этого нового компонента.
Вот как выглядит законченный продукт, цель нашей работы:
Одно дополнительное замечание: мы не будем реализовывать этот компонент в системе резервирования билетов, поскольку для него нет соответствующего места. Те не менее, используйте его в своих собственных приложениях, если видите, что он подходит.
Для создания JMenuButton нам нужны четыре компонента:
Как я подчеркивал в разделе "Создание абсолютно нового компонента", простейший способ создать новый компонент - добавить существующие компоненты в JPanel. В нашем примере мы будем использовать JPanel в качестве базы для компонентов и поместим две кнопки в верхней части. Фактически, наши JMenuButton являются подклассами JPanel, а не какого-либо компонента, которые он содержит. Выбор схемы для наших компонентов достаточно понятен, и мы можем просто использовать BorderLayout
.
|
Третьим шагом в создании нового компонента является обеспечение его правильной работы. В данном примере нам нужно получить кнопку со стрелкой для запуска всплывающего меню при каком-либо нажатии на нее. Обратите внимание на то, что главная кнопка не обязательно должна что-либо делать внутри. Снаружи ее необходимо прослушивать, но внутри она не меняет своего состояния или внешний вид компонентов и, таким образом, может быть проигнорирована на этом шаге. Также могут быть проигнорированы индивидуальные JMenuItems в JPopupMenu - мы даже не добавили ни одного из них при старте, а потому они не меняют компонент.
Вот код, который будет обрабатывать внутреннее взаимодействие компонентов:
|
Последний шаг является, возможно, самым важным в создании нового компонента - вы должны заключить компонент в легкий для использования общедоступный API, для того чтобы другие люди, которые хотят использовать ваш компонент, смогли легко это сделать. Какой смысл создания чего-нибудь нового, если вы не позволяете никому это использовать? В конце концов, творчество гениев должно быть доступно всем.
Каждый раз, создавая общедоступный API-конверт (wrapper) вокруг вашего компонента, вы должны стремиться сохранить формат таким же, как существующие Swing-компоненты - использовать методы get/set и попытаться не менять любое поведение, которое UI-разработчики ожидали бы от Swing-компонента (например, не меняйте каких-либо метод в JComponent, от которого UI-разработчики будут ожидать одинаковых действий от компонента к компоненту).
Существует две области, которыми нам необходимо интересоваться в нашем JMenuButton. Во-первых, мы должны иметь возможность добавлять альтернативные варианты в JMenuButton. Мы можем создать новый метод для этого:
|
Во-вторых, мы должны иметь возможность обрабатывать события активности, которые могли бы активизировать главная кнопка и альтернативные варианты - в итоге, пользователи должны знать, когда нажимается кнопка или выбирается альтернативный вариант.
|
Все. Это конец процесса создания JMenuButton. В течение каких-нибудь нескольких секунд мы создали новый, функциональный Swing-компонент. Следуя четырем шагам, описанным в данном разделе, вы тоже можете создать ваш собственный компонент.
JMenuButton не предназначался быть профессиональным и совершенным компонентом - он просто служит примером. Следовательно, если вы собираетесь использовать его в ваших собственных приложениях, я бы предложил дополнительные public-методы и дальнейшее тестирование.
Одной из прекраснейших возможностей Swing является его способность легко изменять внешний вид и поведение приложения. Внешний вид и поведение приложения - это комбинация двух вещей (запомните эти термин, так как я буду ссылаться на них во всем разделе):
Внешний вид и поведение приложения управляется Swing-классом javax.swing.LookAndFeel
; мы будем ссылаться на его экземпляр как LookAndFeel . Есть маленькое, но заметное отличие между видом и поведением, которое мы исследуем подробно при рассмотрении Synth.
Еще одной иронией является работа пользовательских LookAndFeel
в Swing: необходимые для создания нового класса javax.swing.LookAndFeel
концепции и код являются сложными (и выходят за рамки данного руководства), но как только javax.swing.LookAndFeel
получен и спакетирован, чрезвычайно легко использовать его в вашем приложении.
Фактически, вы можете изменить внешний вид и поведение вашего приложения полностью, добавив только одну строку кода!
|
Swing устанавливается с несколькими предварительно установленными LookAndFeel. Они соответствуют распространенным на рынке операционным системам, но имеют печальный побочный эффект: LookAndFeel, подходящий для Windows XP, доступен только на Windows, Macintosh LookAndFeel доступен только на Mac OS, а GTK LookAndFeel доступен только на Linux-системах. Sun также создала Ocean LookAndFeel - попытка предоставить хорошо смотрящийся кросс-платформенный внешний вид и поведение.
Давайте посмотрим, как наша система резервирования билетов выглядит в различных установленных LookAndFeel (сюда не входят Mac или GTK, поскольку я пишу это руководство на Windows-машине). Помните, что только одна строка кода должна быть изменена для полного изменения внешнего вида нашей системы резервирования билетов.
Windows LookAndFeel:
Motif LookAndFeel:
И Ocean LookAndFeel:
Самым простым способом начать изменять внешний вид и поведение ваших приложений является изучение использования UIManager. UIManager обеспечивает доступ ко всему, что имеет дело с установленным внешним видом и поведением. Я имею в виду все . Каждый возможный цвет, каждый возможный шрифт и каждая возможная граница может быть изменена и обработана UIManager. UIManager действует как HashTable
, содержащий все эти значения и связывающий их в String
, которые являются ключами во взаимосвязи hashtable.
Здесь находится трудная часть работы с UIManager - эти String
, выступающие как ключи, не документированы где-либо на сайте Sun. К счастью, исследуя установленные LookAndFeels, поставляемые с Swing, вы можете увидеть, какие ключи использует Sun, и использовать их самостоятельно. Плохо, что эти ключи нигде не документированы, поскольку это только повышает сложность создания нового пользовательского внешнего вида и поведения.
Мы будем использовать UIManager для изменения цветов меток с синего на зеленый, а также шрифта, который используется во всем приложении:
|
Вот наше приложение:
Естественно, такой тип кодирования не соответствует объектно-ориентированному программированию, поскольку вы не хотели бы набирать заново эти строки кода в каждом приложении, использующем данный внешний вид и поведение. Кроме того, вы не можете легко позволить другим использовать ваше новое произведение подобным способом.
Решением Sun является предоставление класса javax.swing.LookAndFeel
, который позволяет вам легко упаковать всю информацию, необходимую для создания внешнего вида и поведения для вашего приложения. Он также позволяет описать, как объединить все фрагменты для создания внешнего вида и поведения, которое вы хотите распространять.
Существует два способа создать LookAndFeel. Первый - создать подкласс самого класса javax.swing.LookAndFeel
, хотя это более трудный вариант. Лучшее решение - создать подкласс одного из существующих LookAndFeel, предоставляемых Swing: либо javax.swing.plaf.metal.MetalLookAndFeel
, либо javax.swing.plaf.basic.BasicLookAndFeel
(строительный блок для внешнего вида и поведения, не имеющий каких-либо визуальных элементов, а служащий в качестве основы для создания других LookAndFeel).
Имеется также некоторая базовая информация, которую должен содержать каждый LookAndFeel. Это информация, рассказывающая Swing о LookAndFeel и позволяющая другим знать о нем, если вы решите упаковать его и распространять.
getDescription()
: Описание внешнего вида и поведения.
getID()
: Уникальный ID, который может быть использован для идентификации внешнего вида и поведения.
getName()
: Название внешнего вида и поведения.
isNativeLookAndFeel()
: Указывает, являются ли этот внешний вид и поведение родными для OS; любой пользовательский внешний вид и поведение должны возвращать значение true
из этой функции.
isSupportedLookAndFeel()
: Тоже должна возвращать значение true
для любого пользовательского внешнего вида и поведения. Взгляните на этот код в нашем новом пользовательском классе LookAndFeel
, FlightLookAndFeel
.
|
Последним шагом в создании класса FlightLookAndFeel
является описание того, что должны менять внешний вид и поведение. В примере, рассмотренном в разделе "UIManager", мы просто переопределили определенные цвета и шрифты, которые уже были в MetalLookAndFeel
. Мы будем повторно использовать этот код для создания нашего внешнего вида и поведения.
Существует класс, работающий аналогично UIManager, называемый UIDefaults
. Различие между ними едва различимо - UIManager должен быть использован вне классов LookAndFeel
, поскольку он представляет все значения внешнего вида и поведения после своей загрузки. UIDefaults
представляет эти же значения во время загрузки.
Класс LookAndFeel
имеет метод getDefaults()
, который загружает эти значения, а затем использует их для создания внешнего вида и поведения. Для того чтобы получить здесь наши собственные значения вместо значений Metal
, которые мы переопределяем, необходимо сначала вызвать функцию MetalLookAndFeel
getDefaults()
для загрузки каждого значения, которые мы не переопределяем (если мы этого не сделаем, то получим непонятные внешний вид и поведение, отсутствующие цвета, размеры и т.д.). После предоставления MetalLookAndFeel
возможности первому создать все, что ему необходимо, мы можем просто переопределить желаемые значения и создать FlightLookAndFeel
таким, каким мы хотим его видеть.
|
Хотя предыдущих разделов было достаточно для создания пользовательского внешнего вида, имеющего дело только с цветом и шрифтами, на самом деле, это не совсем полный урок по созданию завершенного пользовательского внешнего вида и поведения, которые можно было бы сравнить с WindowsLookAndFeel
или MotifLookAndFeel
. Они выходят далеко за рамки простого изменения цвета и шрифтов; они изменяют практически все в реализации по умолчанию - способ прорисовки каждого компонента, как он реагирует на события от мышки, где размещаются компоненты при добавлении элементов и т.д. Создание завершенного внешнего вида и поведения - это не простая задача.
Данное руководство не будет рассматривать большое количество шагов, необходимых для создания пользовательского внешнего вида и поведения. Фактически, для обучения созданию пользовательского вида и поведения могло бы быть написано отдельное руководство; к сожалению, это сложное предприятие. Чтобы вы понимали смысл вещей, отмечу, что эта задача требует от пользователя создания нового класса для каждого Swing-компонента с указанием того, как он должен выглядеть и вести себя. Это примерно 60 классов, которые вы должны создать, - совсем не то, что можно сделать после обеда, и конечно же не то, что можно было бы кратко изложить в нескольких разделах данного руководства.
Дальнейшим доказательством сложности создания завершенных пользовательского вида и поведения является то, что количество примеров, доступных в Интернете, удивительно не велико. Всего лишь от 20 до 30 коммерческих LookAndFeel доступны для загрузки - и это для Swing, который присутствует на рынке семь лет.
К счастью, в J2SE 5.0 Swing представил новый класс, который делает процесс намного проще и уменьшает время, необходимое для создания пользовательского внешнего вида и поведения с трех месяцев (как это требовалось в 1.4) до трех недель (как это требуется сейчас). Мы обсудим это в следующем разделе.
Synth - это новейшее LookAndFeel-дополнение к Swing, но есть определенного рода искажение в наименовании. Он, на самом деле, вовсе не LookAndFeel-компонент, что вы обнаружили бы, если бы пытались добавить его в ваше приложение: оно стало бы абсолютно белым. При использовании Synth вы даже не сможете полностью изменить вид и поведение приложения. Synth позволяет вам изменять только внешний вид приложения - границы, цвета, шрифты. Он не позволяет изменять поведение приложения.
Synth, в двух словах, является наружной оболочкой, которую вы устанавливаете в вашем приложении. Эта оболочка содержит информацию, описывающую, как использовать внешние изображения и пользовательский код рисования для создания внешнего вида, выделяя эту информацию из одиночного XML-файла, загружаемого тогда, когда Synth LookAndFeel устанавливается в приложение. Самым большим его преимуществом является предлагаемая пользователям экономия времени - вместо необходимости создавать подклассы для 60 Java-классов вы просто должны создать один XML-файл и некоторые графические элементы. Результат - потенциальное взрывоподобное увеличение количества пользовательских LookAndFeel, доступных разработчикам для выбора, что является конечной целью Sun при выпуске этого нового класса. В случае если вы не следили за драмой, знайте, что Swing годами критиковался за ужасный внешний вид. Ocean LookAndFeel отчасти смягчил это недовольство, а Synth предлагает творческим разработчикам увеличить их потенциал для создания очень красивых LookAndFeel в ближайшем будущем.
Полный урок по использованию Synth и информация по тому, как он будет вписываться в ваше приложение, приведены в моей статье "Synth для профессионалов", опубликованной несколько месяцев назад. В ней в пошаговом режиме рассмотрен процесс создания нового пользовательского внешнего вида и поведения с использованием Synth.
В данном руководстве рассмотрены некоторые проблемы среднего уровня сложности, с которыми вы можете столкнуться при разработке UI-приложений с использованием Swing. Руководство основано на материалах руководства "Введение в Swing", либо на уже имеющихся у вас знаниях Swing. Рассматриваются некоторые общие вопросы, которые возникают у разработчиков UI-приложений. Я попытался выбрать комбинацию наиболее интересных и наиболее важных проблем.
Как я уже говорил много раз в обоих руководствах, они ни коим образом не являются исчерпывающими в своем охвате Swing - есть еще так много материала для изучения, что его не возможно сжать в два учебника. Надеюсь, что полученная вами информация не только улучшит ваши знания в нескольких областях Swing, но и возбудит интерес к Swing в целом и убедит вас глубже исследовать некоторые его стороны.
В данном руководстве за несколько кратких уроков мы преобразовали пример приложения (систему резервирования билетов) из элементарного приложения, просто предоставляющего функциональность, в более профессиональное приложение, решающее все проблемы, с которыми приходится иметь дело Swing-приложениям. Мы начали с приложения, которое выглядело примерно так:
И преобразовали его в такое:
Для полноты изложения сделаем обзор важных уроков, которые могут быть извлечены из этого руководства:
TableRenderer
s: Вы узнали, что вы не ограничены встроенной в JTable схемой цветов для ее ячеек. Создавая ваш собственный TableRenderer
, вы можете управлять внешним видом каждой ячейки таблицы, когда она выбрана или не выбрана, и даже тем, как предоставить информацию о приложении пользователю (например, когда мы затемнили недоступные рейсы).
TableModel
: Кроме управления данными TableModel
может также управлять выравниванием данных в столбцах и возможностью редактирования ячейки.
TableEditor
s: JTable имеет много встроенных редакторов для традиционных классов, появляющихся в данных (String
, Integer
), но создание редактора для других данных является трудной задачей и выходит за рамки данного руководства.
TableSorter
: Пользователи ожидают, что у них будет возможность сортировать данные в их таблицах путем нажатия на заголовки столбцов. Поставляемая в Swing JTable не поддерживает сортировку вообще, но Sun предоставляет класс TableSorter
(не поставляемый с JDK), который берет на себя заботу о сортировке. SwingUtilities.invokeLater()
должен использоваться всегда, когда UI-работу должен выполнить поток, отдельный от EVT (event-dispatch thread).
SwingWorker
(еще один класс, не поставляемый с JDK, но опубликованный Sun) должен всегда использоваться с EVT при выполнении затратного по времени действия (например запроса к базе данных). paintComponent()
для адаптации компонента к вашим требованиям. Возможность создания нового пользовательского компонента позволяет вам создавать оригинальные необычные компоненты, отсутствующие в Swing. Для создания нового компонента необходимо выполнить несколько действий: указать нужные вам компоненты, расположить их соответственно, настроить их на совместную работу друг с другом для корректной работы всего нового компонента, и, наконец, заключить его в общедоступный API для упрощения работы с ним.