Четыре способа построения мобильного приложения, часть 4: платформа Appcelerator Titanium.

В трех предыдущих статьях мы рассмотрели, как создать приложение-калькулятор для расчета чаевых на базе родной iOS платформы, родной Android платформы и PhoneGap. В этой статье мы рассмотрим еще один кросс-платформенный инструмент для разработки под названием Appcelerator Titanium.

PhoneGap позволил нам быстро создать приложение Tip Calculator и запустить его, как на Android, так и на iOS платформе. При этом мы старались сохранить все особенности пользовательского интерфейса (UI). Все созданные нами элементы пользовательского интерфейса вполне пригодны для работы, но они не могут предоставить такого же опыта пользовательского взаимодействия, как родные приложения. В основе нашего решения на основе PhoneGap лежало использование Веб-представления и визуализации пользовательского интерфейса с помощью HTML5 и CSS3.

Для сравнения, Appcelerator Titanium приложения визуализируют пользовательский интерфейс с использованием собственных средств управления платформой. Titanium позволяет воссоздать опыт родного UI, наряду с портативностью платформ iOS, Android и BlackBerry. Немного позже, в течение этого года команда Appcelerator планирует реализовать поддержку для Windows Phone и Windows 8.

Далее представлена Appcelerator Titanium версия нашего примера FasTip приложения, запущенного как на iOS, так и на Android:

FasTip application on iOS.

Приложение FasTip на iOS. (Увеличенная версия изображения).

FasTip application on Android.

Приложение FasTip на Android. (Увеличенная версия изображения).

При использовании Titanium, приложение будет написано на JavaScript. Однако вместо манипулирования HTML DOM, как это было сделано в PhoneGap приложении, JavaScript взаимодействует с API Appcelerator Titanium. Titanium позволяет использовать различные элементы пользовательского интерфейса для создания таких вещей, как кнопки, текстовые поля, списки и так далее. Точность представления этих объектов поддерживается соответствующими элементами управления мобильной платформы. Во многих случаях, написанный вами код для одной платформы без модификации сможет работать и на других платформах. Однако, как мы видели в различных подходах, связанных и с iOS, и с Android, в навигационных экранах (например, ViewControllers с NavigationController на iOS и Activities в Android) не все из существующих различий могут быть ликвидированы должным образом. Таким образом, даже при использовании Appcelerator, по крайней мере, некоторые части кода необходимо будет написана специально для конкретно взятой платформы.

Appcelerator состоит из набора различных продуктов, в том числе Titanium SDK для создания мобильных приложений, а также некоторых облачных сервисов, которые могут быть использованы для разработки серверной части приложения. Эти продукты могут быть использованы независимо друг от друга или комбинированно. Titanium SDK и интегрированная среда разработки (IDE) могут быть использованы абсолютно бесплатно, в любой коммерческой деятельности. Appcelerator требует оплаты только Enterprise (для компаний) версии, которая включает дополнительную поддержку, облачные сервисы и аналитические возможности. За дополнительную плату также становятся доступными учебные курсы и программы сертификации.

Appcelerator также предлагает интегрированную среду разработки на основе Eclipse, которая называется Titanium Studio. Эта IDE содержит среду редактирования JavaScript, вместе с отладчиком источником на уровне исходного кода.

Titanium Studio IDE overview.
На этом рисунке вы можете увидеть внешний вид интегрированной среды разработки под названием Titanium Studio. (Увеличенная версия изображения).

На момент написания этой статьи не существовало графических инструментов компоновки похожих на ADT менеджер компоновки для Android или StoryBoards и Interface Builder для Interface для iOS. При использовании Titanium, шаблон экранов создается либо в JavaScript, либо в языке определения XML, как часть Appcelerator Titanium фреймворка Alloy (сплав). Alloy представляет собой фреймворк приложения, который позволяет задать пользовательский интерфейс приложения в XML и применять различные стили к элементам управления в проекте через синтаксис, напоминающий CSS. В GitHub репозитарии я разместил код, как для Alloy, так и для классической версии нашего приложения. В репозитарии также содержатся примеры кода и для других статей из этой серии.

С чего начать ваш первый проект.

Вы можете использовать Appcelerator Titanium и Titanium Studio абсолютно бесплатно. Appcelerator предоставляет документ под названием «Быстрый старт». В этом документе подробно описаны основные шаги, которые позволят вам приступить к работе. Для того чтобы начать пользоваться инструментами Titanium, просто выполните следующие действия:

  1. Для пользования Titanium сначала необходимо Создать бесплатный аккаунт на веб-сайте Titanium. Благодаря этой учетной записи разработчика вы получите возможность использования интегрированных сред Titanium SDK and Titanium Studio IDE.
  2. Если у вас уже есть учетная запись разработчика, то вы имеете доступ к Appcelerator ресурсам разработчика. Здесь вы также найдете серию ссылок о том, как «Приступить к работе», в том числе тех, которые позволят получить все необходимые инструменты для Mac, Windows и Linux.
  3. Так же, как и в случае с PhoneGap, вам придется установки родные SDK (набор инструментальных средств разработки программного обеспечения, включающий библиотеки, заголовочные файлы, help-файлы, документацию и т.п.) пакеты для платформ, которые вы хотите поддерживать. Titanium Studio значительно упрощает эту задачу. Инструмент Platform Configuration (конфигурация платформы) встроен прямо в Titanium Studio и позволяет достаточно легко устанавливать и обновлять родные SDK пакеты:Install the native SDKs
    Установка родных SDK пакетов. (Увеличенная версия изображения). Теперь вы готовы к созданию проекта.
  4. В меню выберите раздел «File» (Файл) и выберите пункт «New» (Новый) → «Mobile App Project» (Проект мобильного приложения). Перед вами откроется диалоговое окно, в котором вы можете выбрать для разработки Alloy или Classic (классический) проект. Как уже упоминалось, Alloy представляет собой фреймворк приложения для Appcelerator, который позволяет задавать пользовательский интерфейс приложения в XML и использовать синтаксис, напоминающий CSS, для стилизации приложения. В классических шаблонах применяется другой подход, который основан на использовании только лишь кода. Appcelerator рекомендует использовать Alloy для большинства новых Titanium проектов. В большинстве примеров из этой статьи будет использован Alloy, потому что его кодовая структура является более управляемой.Choose a development project.
    Выберите проект разработки. (Увеличенная версия изображения).
  5. Выбор «Default Alloy» (Alloy по умолчанию) проекта позволит вам создать простой проект в виде одного экрана с надписью «Hello World» (привет мир). Подобная функциональная возможность может быть полезной отправной точкой. При нажатии кнопки «Next» (Далее) откроется следующее диалоговое окно:
    A simple project with as a useful starting point.
    Этот простой проект может стать полезной отправной точкой. (Увеличенная версия изображения). В приведенном выше примере в поле с именем приложения введено название «MySampleApp». Как только вы нажмете кнопку «Finish» (Готово), Titanium Studio создаст требуемую структуру каталогов проекта и откроет ее в обозревателе проекта.
  6. Запустить приложение так же легко, как и выбрать симулятор в выпадающем меню устройств и нажать кнопку «Run» (Запустить):
    run-on-ios-500
    Запуск приложения. (Увеличенная версия изображения).
  7. Кнопка «Run» имеет свое собственное выпадающее меню, которое обеспечивает возможность для отладки и упаковки приложения.

Организация Appcelerator проекта.

В рамках этой статьи мы акцентируем внимание на Alloy версии приложения. Структура проекта будет выглядеть следующим образом:

Project Structure

Структура проекта.

Проект представляет собой набор XML (расширяемая спецификация языка, предназначенного для создания веб-страниц), TSS (система с разделением времени) (мы вернемся к ней позже) и JavaScript файлов, а также графических активов, необходимых для приложения. При компиляции проекта для соответствующей платформы, компилятор преобразует XML и TSS файлы в JavaScript код. Затем осуществляется совместная упаковка кода и графических активов в пакет развертывания для соответствующей мобильной платформы. XML и TSS файлы во время рабочего цикла не интерпретируются; это необходимы для того, чтобы не создавать большие объемы кода для верстки и стилизации элементов управления. Документация Titanium показывает, как XML и TSS отображены в JavaScript. Если вы знакомы с Adobe Flex разработкой, то XML файлы обрабатываются так же образом, как MXML файлы.

Каждый проект начинается с файла index.xml, который служит в качестве отправной точки приложения. При открытии файла index.xml в каталоге views, мы увидим следующее:

<Alloy>
	<Require id="calculator" src="calculator" platform="android"/>
	<NavigationWindow id="index" platform="ios">
		<Require src="calculator" id="calculator"/>
	</NavigationWindow>
</Alloy>

Эта разметка сообщает компилятору о необходимости загружать содержимое calculator.xml в главное окно. Примечание platform="android" атрибут; каждый элемент в Alloy может сопровождаться platform и/или formFactor атрибутом, который будет указывать на принадлежность элемента к конкретной платформе (Android, iOS и т.д.) или формфактору (конструктивная характеристика, определяющая геометрическую форму и габаритные размеры устройства) (КПК или планшеты).

После Require элемента для Android, мы видим такой же Require элемент, но завернутый в элемент NavigationWindow. Как уже упоминалось, Android и iOS имеют различные механизмы для перемещения пользователя между экранами. В этом случае, NavigationWindow соответствует UINavigationController, с использованием которого мы привыкли строить родные iOS приложения. Android не имеет соответствующего навигационного элемента, и поэтому переходы обрабатываются по-разному. SDK Titanium дает возможность разработчику использовать обе эти формы навигации. Разработчику теперь не нужно пытаться абстрагироваться, выискивая подход с общим наименьшим знаменателем. SDK Titanium обеспечивает более прямой доступ к родным элементам управления, но от разработчика все равно требуется учитывать существующие различия. В результате, в нескольких местах нашего кода мы должны учитывать требования каждой платформы.

Для каждого представления у нас есть соответствующий файл контроллера. Далее представлено содержимое файла index.js:

if (OS_IOS) {
	Alloy.Globals.navgroup = $.index;
}

if (OS_ANDROID) {
	$.calculator.getView().open();
} else {
	$.index.open();
}

Этот файл начинается с кода, который проверяет специальные переменные. Благодаря этим переменным Alloy указывает платформу, на которой должно работать приложение. Для iOS мы присваиваем ссылку на NavigationWindow объект в Alloy.Globals объекте. Alloy.Globals – это просто общее пространство имен, в котором вы можете хранить объекты. Эти объекты будут доступны в любом месте приложения. В этом случае мы сохраняем ссылку на NavigationWindow, чтобы затем иметь возможность получить к нему доступ в любом месте приложения. Последний раздел кода открывает контроллер, который будет отображать главный вид калькулятора. Для Android мы будем открывать представление на контроллере напрямую. В случае iOS, представление будет находиться в NavigationWindow, поэтому мы должны открыть NavigationWindow.

Определение элементов пользовательского интерфейса.

На первом экране нашего приложения, пользовательский интерфейс определяется views/calculator.xml файлом. Он содержит элементы, которые описывают на экране каждый объект TextField, Button, Label и так далее.

<Alloy>
	<Window title="Alloy FasTip" backgroundColor="#fff">
		<!-- Android menu -->
		<Menu platform="android">
			<MenuItem
					onClick = "clickedSettings"
					title = "Settings"
					icon = "Ti.Android.R.drawable.ic_menu_preferences"
					showAsAction = "Ti.Android.SHOW_AS_ACTION_IF_ROOM" />
		</Menu>

		<!-- rightNavButton for platforms that support it -->
		<RightNavButton platform="ios">
			<Button id="settingsButton"
					onClick="clickedSettings"
					title="Settings" />
		</RightNavButton>

		<TextField id="billAmtTextField"
					top="40"
					width="146"
					height="35"
					textAlign="right"
					hintText="Bill amount"
					keyboardType="Ti.UI.KEYBOARD_DECIMAL_PAD"
					returnKeyType="Ti.UI.RETURNKEY_DONE" />

		<Button id='calcTipButton'
				onClick="clickedCalculate"
				title="Calculate Tip"
				top="100" width="122"
				class="primaryButton" />

		<View top="170" width="Ti.UI.SIZE" height="Ti.UI.SIZE">
			<Label left="13" width="124"
					top="0"
					textAlign="Ti.UI.TEXT_ALIGNMENT_RIGHT">
				Tip Amount:
			</Label>
			<Label id="tipAmtLabel" left="130"
					top="0" width="80"
					textAlign="Ti.UI.TEXT_ALIGNMENT_RIGHT">
				$0.00
			</Label>
			<Label left="13" width="124" top="30"
					textAlign="Ti.UI.TEXT_ALIGNMENT_RIGHT"
					class="totalAmt">
				Total Amount:
			</Label>
			<Label id="totalAmtLabel" left="130"
					top="30" width="80"
					textAlign="Ti.UI.TEXT_ALIGNMENT_RIGHT"
					class="totalAmt">
				$0.00
			</Label>
		</View>
	</Window>
</Alloy>

В верхней части файла, мы можем увидеть, как реализована разница между платформами. Android поддерживает все те меню, которые мы видели в статье родном Android, и которые появляются в панели действий на Android 4.x устройствах. Этот элемент меню отвечает за выполнение настройки.

Android Action Bar.

Панель действий Android.

<!-- Android menu -->
<Menu>
	<MenuItem
			onClick = "clickedSettings"
			title = "Settings"
			icon = Ti.Android.R.drawable.ic_menu_preferences
			showAsAction = Ti.Android.SHOW_AS_ACTION_IF_ROOM />
</Menu>

Элемент меню относится только к Android, поэтому не стоит выполнять его оформление с использованием атрибутов характерных для конкретно взятой платформы, например, platform="android". В iOS этот элемент будет проигнорирован компилятором.

Чтобы кнопка настройки отображалась в iOS, мы должны добавить ее в панель кнопок навигационного контроллера iOS. Это достигается при помощи следующего элемента с атрибутом, который ограничивает его использование только для iOS (platform="ios"):

iAndroid Navigation Controller

Навигационный контроллер Android.

<RightNavButton platform="ios">
	<Button id="settingsButton" onClick="clickedSettings"
			systemButton="Titanium.UI.iPhone.SystemButton.COMPOSE" />
</RightNavButton>

В представленном выше фрагменте вы можете увидеть атрибут systemButton в Button элементе, который идентифицирует один из стандартных для iOS стилей кнопок. Если у вас есть такое желание, то можно удалить его и поставить текстовый заголовок или другое подходящее изображение.

Обратите внимание, что в обоих случаях, будь то пункт меню или кнопка навигации, вызов функции происходит через атрибут onClick. Код для этого экрана контролируется JavaScript файлом с тем же именем, что и .xml файл. Фреймворк Alloy выполняет роль контроллера, и в соответствии с соглашением устанавливается в controllers/calculator.js файле. Alloy следует набору соглашений, который устанавливает, где находятся все файлы в структуре каталогов и как они соотносятся друг с другом.

Первая функция в нашем файле calculator.js обрабатывает событие щелчка, когда пользователь нажимает на кнопку «Calculate tip» (рассчитать чаевые):

function clickedCalculate(e) {
	var tipPct = Ti.App.Properties.getDouble('tipPct', .15);
	var billAmt = parseFloat($.billAmtTextField.value) || 0;
	var tipAmt = billAmt * tipPct;
	var totalAmt = billAmt + tipAmt;
	$.tipAmtLabel.text = '$' + tipAmt.toFixed(2);
	$.totalAmtLabel.text = '$' + totalAmt.toFixed(2);
	$.billAmtTextField.blur();
}

Ti.App.addEventListener('recalc', clickedCalculate);

Для расчета чаевых мы сначала попытается извлечь установленный по умолчанию процентный показатель из Ti.App.Properties. Этот объект очень похож на тот, который мы использовали в трех других реализациях: NSUserDefaults в iOS, SharedPreferences в Android и LocalStorage в PhoneGap. Элементы управления, которые мы определили в calculator.xml файле становятся свойствами в этом контроллере, и мы можем ссылаться на них, используя $.<controlId> формат. Сам расчет очень похож на код, который мы использовали для PhoneGap. Это объясняется тем, что во всех случаях использовался JavaScript.

В контроллер calculator.js с помощью метода Ti.App.addEventListener мы также добавили событие слушателя. Этот шаг позволит нашему коду реагировать на события, которые происходят в любом месте приложения. Когда пользователь покидает пределы окна, мы будем запускать recalc событие из настроек экрана. Таким образом, калькулятор всегда будет получать актуальные данные о процентных показателях. Вскоре мы увидим, что настройки экрана не содержат прямой ссылки на калькулятор. По этой причине калькулятор не может вызывать методы непосредственно из настроек. Таким образом, использование обработчиков событий является хорошим способом разделения связей между контроллерами приложения. Это позволяет им обмениваться информацией, но без использования прямых ссылок друг на друга. Эти события сопровождаются падением производительности, поэтому если события должны быстро сменять друг друга, необходимо использовать прямую функцию обратного вызова.

Перемещение между экранами: как учесть особенности платформ.

Следующий код будет запускаться в тот момент, когда пользователь нажмет на кнопку «Настройки»:

function clickedSettings(e) {
	var settingsController = Alloy.createController('settings');
	var win = settingsController.getView();

	if (Alloy.Globals.navgroup) {
		Alloy.Globals.navgroup.openWindow(win);
	} else if (OS_ANDROID) {
		win.open();
	}
}

Как только пользователь нажимает кнопку «Настройки» в калькуляторе, происходит вызов clickedSettings метода на контроллере. Мы создаем экземпляр settingsController и получаем ссылку на его представление. Объект Alloy.Globals проверяется на наличие navgroup ссылки. Напомним, что эта возможность была установлена для iOS приложений в index.js файле при первом запуске приложения. Теперь мы можем использовать navgroup для открытия окна настроек. Android не имеет navgroup объекта, поэтому открытие окна будет осуществляться независимо.

Экран настроек.

Экран настроек определяется в файле settings.xml, и его содержимое очень похоже на то, что мы видели в файле calculator.xml:

<Alloy>
	<Window title="Settings" backgroundColor="#fff">
		<!-- Android menu -->
		<Menu platform="android">
			<MenuItem
					onClick="clickedDone"
					title="Done"
					icon = "Ti.Android.R.drawable.ic_menu_save"
					showAsAction = "Ti.Android.SHOW_AS_ACTION_IF_ROOM" />
		</Menu>

		<Label top="35">Set tip percentage</Label>

		<TextField id="tipPctTextField"
					top="72"
					width="60"
					height="35"
					textAlign="right"
					hintText="Tip %"
					keyboardType="Ti.UI.KEYBOARD_DECIMAL_PAD"
					returnKeyType="Ti.UI.RETURNKEY_DONE"
				></TextField>
		<Label top="72" left="195">%</Label>

		<Button id='saveButton'
				onClick="clickedSave"
				title="Save Settings"
				top="120" width="130"
				class="primaryButton" ></Button>
	</Window>
</Alloy>

Так же, как и с представлением калькулятора, существует два различных подхода к добавлению кнопки в панель действий (в случае Android) или в панель навигации (в случае iOS). Как мы увидим во фрагменте кода ниже, кнопка «Готово» и кнопка «Сохранить» подключены к соответствующим методам контроллера.

После нажатия кнопки «Сохранить» в settings.js осуществляется запуск следующего кода:

var tipPct = parseFloat( $.tipPctTextField.value );
if (tipPct > 0) {
	// Persist the new percentage value
	Ti.App.Properties.setDouble('tipPct', tipPct / 100);
	Ti.API.log('info', 'Settings saved');
	closeSettings();
} else {
	var dialog = Ti.UI.createAlertDialog({
				message: 'Enter a tip percentage greater than zero',
			ok: 'Try again',
			title: 'Invalid percentage'
			}).show();
}

Чтобы сохранить процентное значение для использования в будущем, мы используем объект Ti.App.Properties.setDouble, который сохраняет значение между запусками нашего приложения. Для отладки, мы регистрируем сообщение через Ti.API.log. Это сообщение будет записано в журнал устройства. Это по сути то же самое, что и запись сообщения для console.log() в браузере.

Следующий фрагмент кода имеет очень важное значение, поскольку он касается закрытия окна:

function closeSettings() {
	Ti.App.fireEvent("recalc");
	var settingsWindow = $.getView();
	if (Alloy.Globals.navgroup) {
		Alloy.Globals.navgroup.closeWindow(settingsWindow, {animated:true});
	} else {
		settingsWindow.close();
	}
}

В этом случае мы, прежд …

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.