Дорога из желтого кирпича: строим пользовательские интерфейсы вместе с Flash 9 & ASWing. Часть 1

Вот и вышел новый flash cs3. Почти два года разработки, покупка macromedia компанией adobe, множество обещаний при разработке следующей версии учесть пожелания, как программистов, так и дизайнеров, и все это закончилось грандиозным ничем. Эта статья начнет серию материалов, в которых я буду рассказывать про хорошие идеи хороших людей, которые не ждут манны небесной от adobe, а создают библиотеки, языки и методики программирования на flash-платформе, пытаясь придать ей более профессиональный вид.

Сразу предупреждаю: все, о чем я пишу и рассуждаю далее, я рассматриваю с точки зрения только программирования, но не дизайна во flash. Когда adobe купила macromedia, было множество заявлений о грядущих перспективах широкого внедрения flash, но не как программы для создания роликов на веб-сайтах или мультиков с масяней. А именно как платформы, работающей и в среде браузера, и в мобильных устройствах, и, более того, даже проникновение на рынок desktop-приложений. Flash как среда, тесно проинтегрированная с html/css/pdf для построения и настройки внешнего вида. Среда, обладающая развитыми методиками взаимодействия с веб-сайтами и веб-службами, социальными сетями. Среда, которая могла бы реально сгладить различия между веб-приложениями и настольными (desktop) программами. Ладно-ладно, я слышал эти обещания еще в далеком 98-м, но сейчас мир, похоже, действительно меняется. Подтверждали эти намерения и выход alpha-версии apollo, и быстрый выход серии средств разработки flex. Почувствовав запах денег, зашевелились microsoft и sun. Microsoft предлагает среду разработки silverlight, основанную на интеграции .net|xaml и возможностей браузера. Sun, уже имея неудачный опыт выхода на этот рынок с технологией jnlp, пытается сделать вторую попытку и представляет javaFX. Было бы неплохо, если бы у нас была возможность удобно устроиться в кресле и понаблюдать, чем закончится битва титанов, но вся проблема в том, что нам нужно писать код и разрабатывать веб-приложения сейчас. А чем сложнее задача, тем больше для нас важны не рекламные проспекты пиар-отделов и пространные статьи евангелистов каждого из подходов. Достаточно опытному программисту неинтересны статьи с примерами из пяти строк, показывающие, как легко он будет решать свои задачи. Ему более важны примеры того, как он будет избегать тех ошибок и проблем, с которыми уже столкнулся раньше. Значит, нам нужны отлаженные, удобные и простые средства, для того чтобы быстро и удобно писать код, быстро компилировать, быстро отлаживать, быстро развертывать и оптимизировать код. Так о чем я буду писать? О среде разработки FlashDevelop, которая в десяток раз удобнее, чем встроенный редактор flash cs3. О языке haxe. О компиляторе MTASC, который работает в разы быстрее, чем стандартный компилятор flash. О библиотеке ASWing для построения пользовательских интерфейсов. О внедрении SVN и ant для организации коллективной работы и автоматизации сценариев. О методах тестирования кода. Обо всем том, что позволяет оставить от всего flash только один flashplayer и наслаждаться этим.

Тема сегодняшней статьи — библиотека создания пользовательских интерфейсов ASWing. Существуют две основные версии этой библиотеки: ASWing2 и ASwing3. Первая из них ориентируется на actionscript2, и вы можете ее использовать для написания кода в macromedia flash 8. Вторая же версия ориентирована на actionscript3, и, следовательно, в список инструментов, где можно применять aswing, входит не только недавно вышедшая версия adobe flash cs3, но и adobe flex 2 и готовящийся к выходу flex 3 (пока он доступен в виде ранних бета-версий). Также aswing3 можно использовать совместно с adobe AIR (в девичестве apollo). Хотя я практически не попробовал ASWing2, но с первого взгляда кардинальных отличий между ASwing2 и ASwing3 не нашел. Проще говоря, ASwing3 — это эволюционный шаг вперед от ASwing2, а вовсе не революционная перестройка. Мне как хорошо знакомому с библиотекой Swing (стандартная java-библиотека служит для построения GUI) сразу показались знакомыми иерархии классов и подход с четким отделением данных от их визуализации. Чем больше я рассматривал примеры и читал документацию, тем больше удостоверялся в том, что идеи swing произвели неизгладимое впечатление на разработчиков ASWing. Я попробовал библиотеку ASwing в паре своих проектов и остался доволен — надеюсь, что и вам она понравится. Домашняя страница библиотеки: сайт

Весь дальнейший код пишется в Flash cs3, хотя с равным успехом мог быть написан в Flex Builder, FlashDevelop или любом другом редакторе. Итак, вы скачали и распаковали архив библиотеки. Отличия только в способе подключения библиотеки. Так, для Flash cs3 вам необходимо зайти в меню Edit -> Preferences -> закладка ActionScript -> кнопка ActionScript 3 Settings. В появившемся диалоговом окне выберите путь к каталогу, где находятся исходники ASwing (это подкаталог исходного архива с названием src). Если вы используете flex builder, то на стадии создания нового проекта вам необходимо указать путь к файлу библиотеки (она называются AsWingA3.swc и находится в подкаталоге bin). После чего вы создаете новый проект с типом ActionScript3 и импортируете несколько основных пакетов библиотеки и выполняете первичную настройку Aswing. В частности, надо запретить масштабирование графического содержимого приложения (за это отвечает свойство stage.scaleMode), а также неплохо отключить выделение текущего элемента с помощью специальной рамки (поведение flash по умолчанию). Затем мы должны начать конструировать собственно интерфейс. Все компоненты ASWing делятся на две категории: собственно компоненты и их контейнеры. Все контейнеры наследуются от класса org.aswing.Container, вот часть их: JList, JMenuBar, JPanel, JPopupMenu, JRootPane, JScrollPane, JSplitPane, JTable, JTableHeader, JToolBar, JToolTip, JTree, JViewport. Сходность названий с названиями аналогичных по функциональности классов в мире java swing — не единственное общее между этими двумя библиотеками. В aswing реализована концепция менеджеров раскладок. Каждый контейнер имеет некоторый привязанный к нему менеджер раскладки, в некоторых ситуациях эти менеджеры можно менять друг на друга, иногда это недопустимо. Идея менеджера раскладки в том, что мы не задаем положение элементов графического интерфейса жестко — мол, эта кнопка будет находиться в таких-то координатах и иметь такой-то размер с точностью до пикселя. Первоначально, когда java ориентировался на выполнение кода на различных платформах с различными реализациями графических элементов, точное позиционирование было скорее зло, чем благо. Согласитесь сами, что внешний вид кнопок, меню и других компонентов отличен для windows, mac или motif. С широким распространением концепции skin'ов для программ или для всей операционной системы в целом идея абсолютного позиционирования элементов GUI не приобрела дополнительных плюсов. Если задуматься еще о задаче создания локализованных версий вашего программного продукта, то становится очевидно, что подход, когда на кнопку Выход отводилось по ширине ровно 50 px, уже не пройдет. Потому что на каком-нибудь языке тумба-юмба слово "Выход" состоит из доброй сотни символов и элементарно не влезает в отведенное ему место. Так что концепция менеджеров раскладок, когда элементы выстраиваются, основываясь на их реальных или предпочитаемых размерах, является наилучшей. Традиционно каждый элемент GUI сообщает менеджеру раскладки, какие размеры являются для него минимально допустимыми, какие максимально предельные, и какие размеры идеальны или предпочитаемые. Менеджер раскладки отводит место для элемента так, чтобы попасть в отрезок между минимальными и максимальными размерами. Но вовсе не факт, что размер будет совпадать с предпочитаемым, ведь надо еще учитывать интересы и соседних элементов, у которых тоже есть свои минимальные, максимальные и предпочитаемые размеры.

Итак, у каждого контейнера есть метод setLayout(layout:LayoutManager) в качестве единственного параметра, которому можно передать менеджера раскладки. Вот полный список этих менеджеров: BorderLayout, BoxLayout, CenterLayout, EmptyLayoutUIResourse, FlowLayout, GridLayout, ScrollPaneLayout, SoftBoxLayout, VerticalLayout, ViewportLayout, WindowLayout. Снова дежа-вю — эти классы я уже многократно видел в java swing. Для тех, кто со swing не работал, мы попробуем сейчас сделать простой пример и показать пару раскладок в действии. Пусть это будет программа- калькулятор. Условно она будет состоять из двух текстовых полей для ввода чисел операндов, четырех кнопок для вызова соответствующих математических операций и текстового поля, в котором будет отображаться результат. Выглядеть конечный результат будет как на рис. 1. Обратите внимание на то, что у меня есть четыре "полосы", расположенные друг на друге, в каждой из "полос" располагается либо пара элементов, текстовая надпись и поле для ввода текста, либо набор из четырех кнопок. Прежде всего, я создаю панель, на которой будет расположено абсолютно все. Но как теперь сказать, что это абсолютно все должно располагаться по вертикали? Нам нужно назначить панели раскладку BoxLayout. В качестве параметров конструктору передается способ раскладки (по вертикали или по горизонтали), а также расстояние между компонентами. Следующий шаг — это добавить на панель еще четыре панели, каждая из которых будет иметь ту же раскладку BoxLayout, но уже горизонтальную. Последний шаг — это размещение собственно текстовых полей, надписей и кнопок внутрь этих четырех панелей. Последним шагом будет вызов метода validate для панели верхнего уровня. Этот вызов служит для того, чтобы все контейнеры попросили свои менеджеры раскладки пересчитать расположение всех дочерних элементов. Всякий раз, когда вы программно изменяете внешний вид приложения, добавляя или удаляя компоненты GUI, желательно вызывать этот перерасчет. Для создания надписи мы используем класс JLabel. "Надпись" умеет отображать не только текст, но и маленькую картинку — "иконку". Эти параметры и следует передать конструктору класса. Для создания иконки я использовал класс LoadIcon, для которого необходимо указать параметр конструктора с именем файла картинки, загрузка файла с картинкой в память будет выполнена автоматически. Как вариант можно использовать класс AttachIcon, в качестве параметра конструктора которому следует передать имя внедренного в библиотеку изображения. После импорта изображения в библиотеку не забудьте разрешить для этой картинки доступ из actionscript и дать ей некоторое имя, которое и будет передано в качестве параметра конструктору AttachIcon.

Интересный прием заключается в использовании специального объекта Loader'а, позволяющего указать, откуда именно будет выполнена загрузка изображения. Так, я пробовал создавать специальный файл swf с набором картинок, выступающий в роли репозитария ресурсов для основного приложения, хотя подход с LoadIcon мне показался все же удобнее. Единственный неприятный момент при работе с иконками — это то, что нельзя один и тот же объект иконки привязять сразу к нескольким JLabel. В этом случае только одна надпись (та, к которой иконка была присоединена последней) будет с иконкой. Для создания текстового поля мы применяем класс JTextField. Кнопка — это класс JButton. Для его создания достаточно указать название кнопки и — необязательный параметр — иконку. Теперь нам следует указать обработчик событий нажатия на каждую из кнопок. Для этого вы должны вызвать метод addActionListener от имени кнопки и передать в качестве параметра этого метода ссылку на функцию, которая и будет вызвана при активации кнопки. В примере ниже я установил как обработчик события для всех четырех кнопок одну-единственную функцию. Дело в том, что код вычисления будет, по сути, идентичен за исключением собственно операции. Следовательно, внутри функции достаточно определить, какая именно из четырех кнопок была активирована, и выполнить для нее небольшое специфическое действие (собственно вычисление). Обратите внимание, что в качестве параметра функции передается специальный объект AWEvent, содержащий сведения о событии, и в частности, об элементе интерфейса, который это событие инициировал. Ссылка на этот элемент хранится в поле target. Так как мы делаем хороший калькулятор, то необходимо выполнить проверку введенных значений операндов на корректность, и, если какое-либо из чисел не указано, необходимо вывести сообщение об ошибке. Забудьте о trace! В составе ASwing есть поддержка всплывающих диалоговых окон. Для этого используется класс JOptionPane. В нем есть два статических метода: showMessageDialog и showInputDialog, — которые служат соответственно для вывода окна сообщения и для создания диалогового окна для ввода некоторой текстовой величины. На рис. 2 показаны примеры различных диалоговых окон. Также не забывайте, что из-за особенностей flash, даже если вы укажете для некоторого диалогового окна признак модальности, все равно вычисление будет продолжаться далее.

// подключаем нужные для работы ASWing библиотеки
import flash.display.*;
import org.aswing.*;
import org.aswing.event.AWEvent;
import flash.text.*;

// отключаем масштабирование рабочей области экрана
stage.scaleMode = StageScaleMode.NO_SCALE;
// запрещаем подсветку текущего элемента
stage.stageFocusRect = false;
// указываем корень дерева всех элементов ASwing
AsWingManager.setRoot(this);

var ico : LoadIcon = new LoadIcon ("ico_fla9_1.PNG");
JOptionPane.showMessageDialog("Сообщение", "Нажмите одну из этих кнопок", onMessageFoo,null, true, ico, JOptionPane.YES|JOptionPane.NO); trace("bar");
JOptionPane.showInputDialog("Сообщение", "Введите ваше имя", onInputFoo, "George",null, true, ico);
function onMessageFoo(r : int):void {trace("got");}
function onInputFoo(r : String):void {trace("enter");}

А вот полный пример кода для приложения калькулятора.

// подключаем нужные для работы ASWing библиотеки, как и в предыдущем примере
var panel_top:JPanel = new JPanel();
panel_top.setLayout(new BoxLayout (BoxLayout.Y_AXIS, 10) );
var panel_1:JPanel = new JPanel();
panel_1.setLayout(new BoxLayout (BoxLayout.X_AXIS, 10) );
var panel_2:JPanel = new JPanel();
panel_2.setLayout(new BoxLayout (BoxLayout.X_AXIS, 10) );
var panel_3:JPanel = new JPanel();
panel_3.setLayout(new BoxLayout (BoxLayout.X_AXIS, 10) );
var panel_4:JPanel = new JPanel();
panel_4.setLayout(new BoxLayout (BoxLayout.X_AXIS, 10) );
var ico_1 : LoadIcon = new LoadIcon ("ico_fla9_1.PNG");
var ico_2 : LoadIcon = new LoadIcon ("ico_fla9_2.PNG");
// создаем надписи
panel_1.append(new JLabel ("Первое число: "));
panel_2.append(new JLabel ("Второе число: "));
// создаем текстовые поля
var txt_a: JTextField = new JTextField ();
panel_1.append(txt_a);
var txt_b: JTextField = new JTextField ();
panel_2.append(txt_b);
// создаем набор из четырех кнопок
var btnPlus : JButton = new JButton ("+", ico_2);
var btnSubtract : JButton = new JButton ("-");
var btnMultiply : JButton = new JButton ("*");
var btnDivide : JButton = new JButton ("/");
// указываем обработчики событий для этих кнопок
btnPlus.addActionListener(__buttonActionAnyOperation);
btnSubtract.addActionListener(__buttonActionAnyOperation);
btnMultiply.addActionListener(__buttonActionAnyOperation);
btnDivide.addActionListener(__buttonActionAnyOperation);
panel_3.append(btnPlus);
panel_3.append(btnSubtract);
panel_3.append(btnMultiply);
panel_3.append(btnDivide);
panel_4.append(new JLabel ("Результат вычислений: ", ico_1));
var txt_c: JTextField = new JTextField ();
panel_4.append(txt_c);
//добавляем созданные панели-полосы на родительскую панель
panel_top.append(panel_1);
panel_top.append(panel_2);
panel_top.append(panel_3);
panel_top.append(panel_4);
panel_top.setSizeWH(400, 140);
// добавляем на stage корневый элемент панели
addChild(panel_top);
panel_top.validate();
// здесь мы определяем обработчики событий
function __buttonActionAnyOperation(e:AWEvent):void {
var btn:JButton = e.target as JButton;
// тест на проверку того, что оба числа были введены
if (txt_a.getText () == "" || txt_b.getText () == "") {
JOptionPane.showMessageDialog("Ошибка", "Обязательно следует ввести оба значения"); return; }
// преобразуем содержимое текстовых полей в число
var a : Number = parseFloat (txt_a.getText ());
var b : Number = parseFloat (txt_b.getText ());
// выполняем проверку введенных строк текста
if (isNaN(a)) {
JOptionPane.showMessageDialog("Ошибка", "Введеное в первое поле значение не является числом"); return; }
if (isNaN(b)) {
JOptionPane.showMessageDialog("Ошибка", "Введенное во второе поле значение не является числом");return;}
var c : Number;
if (btn == btnPlus) {c = a + b;}
if (btn == btnSubtract) {c = a — b; }
if (btn == btnMultiply) {c = a * b;}
if (btn == btnDivide) {c = a / b;}
txt_c.setText(c.toString());}

Полагаю, что первое знакомство с ASWing состоялось. В следующий раз я рассмотрю более сложные и интересные возможности ASWing. Мы попробуем работать с наборами закладок, таблицами, списками, получим представление о модели данных для компонентов ASWing.

black zorro, black-zorro@tut.by


Компьютерная газета. Статья была опубликована в номере 24 за 2007 год в рубрике программирование

©1997-2024 Компьютерная газета