Простейший калькулятор

Автор: Андрей. Категория: Уроки Flex

Первое приложение на Flex

Ну что ж. Первый практический урок будет простой. Немного сложнее «Hello, world!». В нём я расскажу как сделать web-приложение на Flex4 с помощью среды разработки Adobe Flash Builder 4.6. На сегодня для меня именно эта среда разработки кажется наиболее удобной. Поэтому большинство примеров я буду приводить в ней. Вы, конечно же, можете использовать любую среду разработки.

В качестве введения скажу, что очень рекомендую всем начинать любой проект с проектирования, не бросать сразу все силы на программирование. Любая программа — это лабиринт. Проектирование — это взгляд на этот лабиринт с высоты. Согласитесь, после полного обзора выйти из лабиринта значительно легче.

Цель

  • научиться создавать проекты Flex
  • ознакомиться с основными элементами управления
  • ознакомиться с языком MXML
  • научиться создавать MXML-компоненты
  • написать несколько функций на языке AS3

Техническое задание

В нашей программе мы реализуем 4 простых арифметических действия над двумя целыми числами: сложение, вычитание, умножение, деление. Интерфейс программы будет состоять из:

  • 3 текстовых поля: 2 для ввода чисел, одно для отображения результата
  • один выпадающий список для выбора действия
  • кнопка для подсчёта результата

При нажатии на кнопку будет выполняться проверка на заполнение данных и при успешной проверке — вычисление итогового значения. Как вы уже представили, наш калькулятор визуально будет отличаться от стандартного калькулятора с кнопками цифр и кнопками действий. Это сделано, чтобы не усложнять задачу. Теперь нужно расписать, какие классы будут применяться в этом примере.

Во-первых, несложно заметить, что в нашем приложении есть 2 одинаковых элемента управления. Это текстовые поля для ввода первого и второго числа. По условию, т.к. это будут целые числа, в поля можно вносить только цифры. И ограничим длину числа, допустим, 8-ю знаками. Т.е. нам нужен несколько модифицированный элемент управления TextInput.

Остальные элементы управления я буду использовать в изначальном виде. Все действия для них я пропишу в теле главного класса

Решение

Для начала создадим новый проект: Файл -> Создать -> Проект Flex

New project Flex

В поле Имя проекта введём SimpleCalculator. Остальные опции оставим по умолчанию. Нажмём кнопку Готово. Flash Builder 4.6 создал все необходимые файлы для компиляции приложения, подключил все необходимые библиотеки. В общем всю работу для того, чтобы скомпилировать и запустить приложение, среда разработки сделала за нас. Мы видим, что в пакете по-умолчанию находится пока единственный файл SimpleCalculator.mxml. Вкратце скажу, что язык MXML используется платформой Flex. Он создан, чтобы упростить жизнь Flex-разработчикам и дизайнерам интерфейсов. На самом деле компилируются только AS3 классы, поэтому при компиляции все классы MXML переводятся в AS3, а затем уже в бинарный код. Элементы управления мы будем писать преимущественно на MXML.

Итак, как мы решили раньше, нам нужен модифицированный элемент управления для ввода чисел. Т.е. нам нужно расширить стандартный компонент TextInput. Для этого выберем в меню Файл -> Создать -> MXML-компонент

New MXML-component

В поле Имя введём наименование элемента управления NumericInput, а в поле На основе выберем spark.components.TextInput и нажмём кнопку Готово. Мы увидим в дереве новый файл с таким содержанием:

<?xml version="1.0" encoding="utf-8"?>
<s:TextInput xmlns:fx="http://ns.adobe.com/mxml/2009"
	     xmlns:s="library://ns.adobe.com/flex/spark"
	     xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Declarations>
        <!-- Разместить невизуальные элементы (например, службы или объекты значений) -->
    </fx:Declarations>
</s:TextInput>

Удалим блок <fx:Declarations>...</fx:Declarations>, т.к. внутренние объекты в этом классе нам не нужны. Сейчас нам нужно добавить этому элементу несколько свойств. Свойства добавляются перечислением через пробел до завершающей скобки описания (см. ниже). Добавим нашему классу свойство restrict="0-9". Тем самым мы указываем диапазон допустимых символов для этого поля. Я специально взял целые значения, чтобы не усложнять задачу. Для дробных значений нам нужно будет добавить в допустимые символы точку. Всё бы хорошо, но тогда пришлось бы делать проверку на количество точек. Второе условие — ограничить количество символов. Для этого добавим свойство maxChars="8". В итоге получаем такой код:

<?xml version="1.0" encoding="utf-8"?>
<s:TextInput xmlns:fx="http://ns.adobe.com/mxml/2009"
             xmlns:s="library://ns.adobe.com/flex/spark"
             xmlns:mx="library://ns.adobe.com/flex/mx"
             restrict="0-9" maxChars="8">
</s:TextInput>

Настоятельно советую код писать собственноручно, активно используя сочетание клавиш «CTRL + SPACE», а не копипастить. Во-первых, лучше запоминается. Во-вторых, в таком случае среда разработки автоматически прописывает строки импорта и всякие полезные вещи, что ускоряет разработку и помогает избежать всевозможных ошибок.

Видите как просто описывается элемент управления на MXML. Если бы писали на AS3, код получился бы как минимум в 2 раза длиннее и не так выразителен.

Теперь осталось применить наш новый класс на деле. Переходим в основной файл пишем после блока <fx:Declarations/>:

<s:Panel left="10" top="10" title="Простой калькулятор">
    <s:VGroup left="10" top="10" right="10" bottom="10">
        <local:NumericInput id="firstNumber" width="150"/>
        <s:DropDownList id="operation" width="150" prompt="Операция..."/>
        <local:NumericInput id="secondNumber" width="150"/>
        <s:Button label="Вычислить" width="150"/>
        <s:TextInput id="outResult" width="150" restrict=""/>
    </s:VGroup>
</s:Panel>

На самом деле добавлять элементы можно в режиме Дизайн простым перетаскиванием на форму. Но я не сторонник этого метода, потому что возможно когда-нибудь я перейду на бесплатную среду разработки (например FlashDevelop) и тогда всё равно придётся писать всё ручками. Да и автокод не всегда генерируется такой, какой нужно. Вы можете добавлять элементы, как вам удобно.

Давайте теперь разберём написанное поподробнее. Во-первых, заметим, что в этом коде присутствуют 2 блока: Panel и VGroup. Да, многие элементы управления Flex представляют собой контейнеры, в которые помещаются другие элементы или контейнеры. В нашем случае основным контейнером является объект Application, а в нём объект Panel. У контейнера Panel есть срока заголовка, где мы выводим название нашего приложения через свойство title. Остальные свойства определяют положение этого элемента относительно родительского. Проще говоря наш объект Panel будет находится с отступом 10 пикселей от левого края основного контейнера приложения, и с отступом 10 пикселей от верхнего края (свойства left и top соответственно).

Следующий элемент управления VGroup находится внутри уже упомянутого и представляет собой также контейнер, в котором все элементы находится вертикально упорядоченно. Его свойства — это отступы от всех границ родительского контейнера.

И вот мы подошли к нашему элементу управления NumericInput. Первый вопрос, что такое <local:? Если вы работаете в среде Adobe Flasg Builder, в свойствах приложения заметите добавившуюся строку xmlns:local="*". Это объявление пространства имён для пакета, в котором находится файл NumericInput.mxml. По-умолчанию, FB определяет 3 пространства имён: fx, s и mx. Это значит, что вы можете создать элемент управления с одинаковым наименование как из пространства s, так и из пространства mx (например <s:Button/> и <mx:Button/>) И это будут 2 разных элемента управления. Можно также создать свой собственный элемент управления Button и определить для него дополнительное пространство имён.

Новое пока для нас свойство id оказывается обычным идентификатором элемента. Именно через этот идентификатор мы будем обращаться к элементу и его свойствам и значениям. Второе свойство width определяет ширину элемента. Ширина проставлена у всех элементов, поэтому я не буду далее писать про это свойство.

Далее стоит новый элемент управления DropDownList. Это нередактируемый выпадающий список. Значения в списке нельзя изменить при выполнении приложения непосредственно в этом элементе управления. Свойство prompt определяет строку, пока не выбран ни один элемент из списка. Если мы запустим приложение, то увидим, что список пока пуст. Логично, ведь мы не присвоили этому элементу источник строк списка. Это мы сделаем чуть позже.

После второго элемента NumericInput мы видим элемент Button. Кнопка — элемент управления, без которой не обходится, наверное, ни одно приложение. Именно эта кнопка запускает выполнение кода и вычисление результата. Свойство label — это текст на кнопке.

И, наконец, элемент TextInput, куда мы будем выводить результат. Тут я тоже прописал свойство restrict="". Пустая строка обозначает, что в это поле вводить мы ничего не сможем. Вот так выглядит интерфейс нашего приложения:

Flex-приложение "Простой калькулятор"

Следующим шагом будет добавление списка значений с выпадающий список. Это можно сделать несколькими способами. Про все я пока рассказывать не стану, они будут в других уроках. Остановимся на одном — через объект ArrayList, который будет содержать 4 объекта. У каждого объекта будет 2 поля: label и value. Этот объект мы пропишем в блоке <fx:Declarations/>, который служит именно для таких ситуаций. Все объекты, помещённые в этот блок, создаются на этапе создания самого объекта и могут быть использованы на всём протяжении его существования.

<s:ArrayList id="dataProvider"/>
    <fx:Object label="Сложение" value="+"/>
    <fx:Object label="Вычитание" value="-"/>
    <fx:Object label="Умножение" value="*"/>
    <fx:Object label="Деление" value="/"/>
</s:ArrayList>

Теперь свяжем элемент DropDownList и объект ArrayList. Связывание данных — это одно из преимуществ Flex. Без дополнительных усилий мы можем указать, что один объект постоянно непрерывно наблюдает состояние второго, и каким-то образом реагирует на изменение его состояния. В MXML это делается очень просто, в AS3 тоже возможно, но немного сложнее. Итак, добавляем новые свойства для элемента DropDownList

<s:DropDownList id="operation" width="150" prompt="Операция..." dataProvider="{dataProvider}" labelField="label"/>

Что мы сделали в этой строке? Мы связали объект operation с объектом dataProvider, а это наш недавно созданный ArrayList. И дополнительно явно прописали, что отображаться в списке будет именно поле label каждого объекта из списка. (Если хотите, можете прописать сюда поле value. Тогда в списке будут отображаться символы операций). Запустив приложение, вы увидите, что теперь в списке присутствуют элементы и мы можем выбрать один их них.

Теперь осталось написать функциональную часть приложения. Вычисление будет происходить после нажатия на кнопку Вычислить. Любые действия на форме, начиная перемещением мыши и заканчивая перетаскиванием объектов, вызывают определённый события Event. Нажатие на нашу кнопку вызовет событие MouseEvent.CLICK (если быть точным, то это событие вызывает нажатие кнопки мыши в любом месте приложения). Чтобы перехватить это событие, мы добавим кнопке такой код:

<s:Button label="Вычислить" width="150" click="button1_clickHandler(event)"/>

Очень рекомендую перед началом своего серьёзного проекта прочитать книгу Роберта Мартина «Чистый код». В этой книге вы найдёте очень много полезных рекомендаций по кодированию. Очень часто в программировании важно не ЧТО вы сделали, а то, КАК вы это сделали.

Мы указали, что событие click будет обрабатываться методом button1_clickHandler(), который нам нужно добавить. Код AS3 можно писать внутри MXML файла, предварительно определив для него блок <fx:Script> таким образом:

<fx:Script>
    <![CDATA[
        protected function button1_clickHandler(event:MouseEvent):void
        {

        }
    ]]>
</fx:Script>

В целом язык AS3 очень напоминает Java. Видим, что и в AS3 есть модификаторы управления доступом (первое слово в объявлении функции. Для методов существуют следующие: public, internal, protected и private). Но в отличие от Java в AS3, тип возвращаемого значения метода пишется в конце строки после двоеточия, а перед названием метода пишется ключевое слово function.

Для начала выполним проверку на заполнение первого и второго числа, а также, выбрана ли операция. Если значения не внесены, то выводим предупреждение и выходим из функции. Оператор return может выполнять различные действия. Изначально он служи для возвращения значения функции. Но также его можно использовать как выход из функции в любом месте. Последовательность проверяемых значений расположим в обратном порядке по приоритету, чтобы в строке errorMessage оставалось более приоритетное значение. В последнем условии мы проверяем длину строки errorMessage, и если сообщение об ошибке имеется, то выводим его и выходим из функции.

protected function button1_clickHandler(event:MouseEvent):void
{
    var errorMessage:String = "";

    if (operation.selectedIndex == -1) errorMessage = "Выберите операцию";
    if (secondNumber.text == "") errorMessage = "Введите 2-е значение"; 
    if (firstNumber.text == "") errorMessage = "Введите 1-е значение";
    if (errorMessage.length > 0)
    {
        outResult.text = errorMessage;
        return;
    }
}

В нашем случае всегда выполняется проверка всех условий, даже если ошибка уже найдена. В серьёзных приложениях стоит выполнять независимые проверки и выводить предупреждения сразу после обнаружения.

После выполнения проверок добавим функционал выполнения операции. Для этого создадим две переменных, в которые запишем вводимые значения. Это нужно для того, чтобы выполнить приведение типов, т.к. в текстовом поле пока у нас находится не число, а строка. Некоторые посчитают, что создание двух новых переменных — это неоправданная трата оперативной памяти, и нужно выполнять преобразования в той строке, где это нужно. Я думаю иначе. Золотое правило: одна строка — одна операция. Не нужно писать километровые нечитабельные строки, ради того, чтобы сэкономить несколько байт оперативной памяти. В наше время это неактуально…

protected function button1_clickHandler(event:MouseEvent):void
{
    var errorMessage:String = "";

    if (operation.selectedIndex == -1) errorMessage = "Выберите операцию";
    if (secondNumber.text == "") errorMessage = "Введите 2-е значение"; 
    if (firstNumber.text == "") errorMessage = "Введите 1-е значение";
    if (errorMessage.length > 0)
    {
        outResult.text = errorMessage;
        return;
    }
 
    var value1:int = int(firstNumber.text);
    var value2:int = int(secondNumber.text);
}

Вычисление итогового значения вынесем в отдельную функцию. Ещё одно правило, которого я придерживаюсь. Функция должна выполнять только одну операцию. Она должна выполнять её хорошо, и ничего другого она делать не должна. По-хорошему, я бы также вынес проверку значений в отдельную функцию, но в этом примере позволю себе этого не делать. Наша функция будет выглядеть так:

private function calcResult(value1:int, value2:int):String 
{
    var result:Number;
				
    switch(operation.selectedItem.value)
    {
        case "+":
	{
	    result = value1 + value2;
	    break;
	}
	case "-":
	{
	    result = value1 - value2;
	    break;
	}
	case "*":
	{
	    result = value1 * value2;
	    break;
	}
	case "/":
	{
	    result = value1 / value2;
	    break;
	}
    }
				
    return result.toString();
}

Здесь я использовал блок switch, которые многие специалисты не рекомендуют применять в своих программах. Да, зачастую там, где есть этот блок, можно применить полиморфизм. Но для первого урока это излишне, да и неплохо просто знать о существовании этого блока. Более того, эта конструкция в AS3 довольно давно работает со строками, в отличие от Java, в которой эта возможность появилась только в версии 1.7.

И в заключение, нам осталось использовать новую функцию и вывести результат на экран. Таким образом, главная функция заканчивается такими строками

protected function button1_clickHandler(event:MouseEvent):void
{
    var errorMessage:String = "";

    if (operation.selectedIndex == -1) errorMessage = "Выберите операцию";
    if (secondNumber.text == "") errorMessage = "Введите 2-е значение"; 
    if (firstNumber.text == "") errorMessage = "Введите 1-е значение";
    if (errorMessage.length > 0)
    {
        outResult.text = errorMessage;
        return;
    }

    var value1:int = int(firstNumber.text);
    var value2:int = int(secondNumber.text);
    var result:String = calcResult(value1, value2);
    outResult.text = result;
}

Результат

Архив с готовым проектом

Первый урок на этом завершён.


Тэги: , ,

Ссылка для вашего сайта.



Комментарии (6)

  • ViNNi_P00H

    |

    урок супер, как и идея сайта, где упор делаеться на практику (мне напомнил javacript.ru)…так держать)
    ждем примеров разработки анимации)
    и хотелось б увидесть статю автора з советами новичкам об литературе, об то что используется на практике чаще всего и.т.п.
    спасибо и удачи с flexfactory;)

    Ответить

    • Андрей

      |

      Спасибо за добрые слова. Другие уроки обязательно будут. Сейчас много работы.
      Кстати, страница с литературой есть на сайте в нижнем меню.

      Ответить

  • KKT

    |

    Отличный урок, жду других =)

    Ответить

  • Grego

    |

    — тут слеш не нужен
    Будет ошибка:
    Тип элемента «fx:Declarations» должен заканчиваться соответствующим концевым тегом
    Element type «fx:Declarations» must be followed by either attribute specifications

    Ошибка при работе с TextInput (проверил на SDK 4.0.0 и 4.6.0):
    Множественные значения инициализатора для свойства по умолчанию «text» типа «String»
    решение https://bugs.adobe.com/jira/browse/SDK-25184

    Ответить

    • Андрей

      |

      Grego, спасибо за внимание к посту. В начале я написал, что блок «Declarations» можно убрать, он не нужен для данного примера.

      Ответить

  • Игорь

    |

    Очень понравился урок, только примеры не работают, ошибка на объявление local: и на добавление в блок fx:Declarations
    Возможно у меня что-то не так с настройками
    Большая просьба — пару индивидуальных уроков платно
    Заранее благодарен

    Ответить

Добавить комментарий

Дополнительно