Статьи

Создание вашего первого приложения с помощью предохранителя

Теперь, когда вы изучили основные понятия Fuse, пришло время применить их на практике и создать приложение. В этом уроке вы узнаете, как разрабатывать приложения с использованием инфраструктуры Fuse. В частности, вы узнаете следующее:

  • Как кодировать, используя UX Markup.
  • Как использовать Observable, Timer и Geolocation API.
  • Как просмотреть приложение, используя предварительный просмотр на рабочем столе и пользовательский предварительный просмотр.

Если вам нужно освежить в Fuse, ознакомьтесь с моим предыдущим постом из этой серии: Представляем Fuse для кроссплатформенной разработки приложений .

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

Предохранитель доступен как для Windows, так и для MacOS. Загрузите и установите правильный установщик для вашей платформы. На странице загрузок они также указывают на плагины Fuse, доступные для различных текстовых редакторов. Установите один для вашего текстового редактора. Плагины Fuse включают завершение кода, определение перехода и просмотр журналов, созданных из приложения, что делает разработку приложений более удобной.

Мы также расскажем, как просмотреть приложение, используя пользовательский предварительный просмотр. Для этого на вашем компьютере должна быть установлена ​​Android Studio или Xcode.

Базовое понимание веб-технологий, таких как HTML, CSS и JavaScript, полезно, но не обязательно.

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

Вот как будет выглядеть приложение:

Конечный выход HIIT Секундомер

Вы можете просмотреть полный исходный код в репозитории GitHub .

Установив Fuse Studio, вы сможете создать новый проект Fuse. Просто откройте Fuse Studio и нажмите кнопку « Новый проект Fuse» . Введите название проекта и нажмите « Создать» :

Создать новый проект Fuse

Это создаст новую папку в выбранном каталоге. Откройте эту папку и откройте файл MainView.ux . По умолчанию он будет иметь только разметку <App> . Обновите его, чтобы включить <Text> , а затем сохраните файл:

1
2
3
<App>
    <Text FontSize=»25″>Hello World!</Text>
</App>

Предварительный просмотр теперь должен быть обновлен указанным вами текстом:

Привет мир выходной

Это основной рабочий процесс разработки в Fuse. Просто сохраните изменения в любом из файлов в каталоге проекта, и они автоматически отобразятся в предварительном просмотре на рабочем столе.

Вы также можете увидеть журналы в нижней панели. Вы можете запустить свой собственный с помощью console.log() , как в браузере. Единственное отличие состоит в том, что вам необходимо JSON.stringify() объекты JSON.stringify() , чтобы увидеть их значение, поскольку реализация console.log() в Fuse может выводить только строки.

Теперь мы готовы построить приложение. Откройте MainView. ux файл и удалите элемент <Text> из ранее. Таким образом, мы можем начать с чистого листа:

1
2
3
<App>
 
</App>

Как и в HTML-документе, стандарт должен включать ресурсы — такие как шрифты, таблицы стилей и сценарии — перед фактической разметкой страницы. Поэтому добавьте следующее внутри элемента <App> :

1
<Font File=»assets/fonts/roboto/Roboto-Thin.ttf» ux:Global=»Thin» />

Это импортирует шрифт, указанный в атрибуте File и дает ему имя Thin . Обратите внимание, что это не делает его шрифтом по умолчанию для всей страницы. Если вы хотите использовать этот шрифт, вы должны использовать его имя ( Thin ) в тексте, к которому хотите его применить.

Вы можете скачать шрифт из репозитория GitHub . После этого создайте папку assets / fonts / robot внутри корневого каталога проекта и поместите в него файл .ttf .

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

Далее мы хотим использовать иконки внутри приложения. В Fuse нет встроенных элементов и наборов иконок, которые позволяют вам это делать. То, что он предлагает, это способ включить существующие шрифты значков в ваше приложение. Поскольку значковые шрифты по сути являются шрифтами, мы можем использовать тот же метод для включения шрифтов:

1
<Font File=»assets/fonts/icons/fa-solid-900.ttf» ux:Global=»FontAwesome» />

Вы можете скачать шрифт со значком из репозитория GitHub или загрузить его прямо с fontawesome.com . Обратите внимание, что не все значки на fontawesome являются бесплатными, поэтому лучше перед их использованием проверить настоящую страницу значков . Если рядом со значком вы видите ярлык «pro», вы не сможете просто использовать его в своем проекте, не заплатив.

Далее нам нужно включить файл JavaScript для этой страницы. Мы можем сделать это, используя элемент <JavaScript> :

1
<JavaScript File=»scripts/MainView.js»/>

Не забудьте создать файл scripts / MainView.js в корне каталога проекта.

Чтобы максимизировать повторное использование кода, Fuse позволяет нам создавать собственные компоненты из существующих. В приведенном ниже коде мы используем <Panel> для создания пользовательской кнопки. Думайте об этом как о div который действует как контейнер для других элементов. В этом случае мы используем его как повторно используемый компонент для создания кнопки.

Предохранитель поставляется со многими элементами. Существуют элементы для разметки контента, такие как <Panel> , элементы для отображения пользовательских элементов управления , страниц и навигации , сценариев и данных , а также примитивы для создания пользовательского интерфейса . Каждый из них имеет свой собственный набор свойств, позволяющий вам изменять данные, представление и поведение.

Чтобы создать повторно используемый компонент, добавьте свойство ux:Class к элементу презентации, который вы хотите использовать в качестве основы. В этом случае мы используем <Panel> в качестве базы. Затем вы можете добавить стиль по умолчанию. Это похоже на стиль в CSS. Margin добавляет пространство за пределами контейнера. Здесь мы указали только одно значение, поэтому это поле применяется ко всем сторонам панели. Color добавляет цвет фона к элементу:

1
2
3
<Panel ux:Class=»ToggleBtn» Margin=»4″ Color=»#007bff»>
     
</Panel>

Внутри <Panel> мы хотим показать текст кнопки. Мы хотим превратить это в повторно используемый компонент, поэтому нам нужен способ передать свойства, когда мы будем использовать этот компонент позже. Это позволяет нам достигать различных результатов, только изменяя свойства.

Внутри <Panel> используйте тип данных значения, которое вы хотите передать в качестве имени элемента, а затем добавьте имя свойства, используя ux:Property . Затем вы можете отобразить значение, предоставленное свойству, используя {ReadProperty PropertyName} , где PropertyName — это значение, которое вы {ReadProperty PropertyName} для ux:Property . Это позволит вам <ToggleBtn> свойство Text всякий раз, когда вы используете компонент <ToggleBtn> .

1
2
<string ux:Property=»Text» />
<Text Value=»{ReadProperty Text}» Color=»#fff» FontSize=»18″ Alignment=»Center» Margin=»20,15″ />

Далее мы хотим предложить пользователю своего рода обратную связь, пока кнопка нажата. Мы можем сделать это с помощью триггеров и аниматоров . Триггеры — это, в основном, прослушиватели событий, в данном случае <WhilePressed> . А аниматоры — это анимация или эффекты, которые вы хотите выполнить, пока активен триггер. Приведенный ниже код сделает кнопку на 10% больше ее исходного размера и изменит цвет. Duration и DurationBack позволяют указать, сколько времени потребуется анимации, чтобы достичь своего пика и достичь его конца.

1
2
3
4
<WhilePressed>
    <Scale Factor=»1.1″ />
    <Change this.Color=»#636363″ Duration=»0.03″ DurationBack=».03″ />
</WhilePressed>

Далее мы создаем компонент <IconBtn> . Как следует из названия, это кнопка, которая показывает только значок в качестве своего содержимого. Это работает так же, как и предыдущий компонент, хотя есть несколько новых вещей, которые мы сделали здесь.

Во-первых, это свойство ux:Name . Это позволяет нам дать имя конкретному элементу, чтобы мы могли обратиться к нему позже. В этом случае мы используем его, чтобы изменить его свойство Color во время нажатия кнопки.

Мы также использовали условный элемент с именем <WhileTrue> . Это позволяет нам отключить <WhilePressed> когда значение is_running является ложным. Мы передадим значение этой переменной, как только перейдем к части JavaScript. Пока знайте, что эта переменная указывает, работает ли таймер в данный момент или нет.

01
02
03
04
05
06
07
08
09
10
<Panel ux:Class=»IconBtn»>
    <string ux:Property=»Text» />
    <Text Font=»FontAwesome» Color=»#333″ ux:Name=»LapText» FontSize=»40″ Alignment=»Center»>{ReadProperty Text}</Text>
    <WhileTrue Value=»{is_running}»>
        <WhilePressed>
            <Change LapText.Color=»#ccc» Duration=»0.09″ /> <!— change text color —>
            <Rotate Degrees=»90″ Duration=»0.02″/> <!— rotate the button by 90 degrees —>
        </WhilePressed>
    </WhileTrue>
</Panel>

Теперь мы можем перейти к основному содержанию. Сначала мы <StackPanel> все в <StackPanel> . Как следует из названия, это позволяет нам «складывать» его дочерние элементы вертикально или горизонтально. По умолчанию он использует вертикальную ориентацию, поэтому нам не нужно явно указывать его:

1
2
3
<StackPanel Margin=»0,25,0,0″ Padding=»20″>
     
</StackPanel>

В приведенном выше коде мы использовали четыре значения для Margin . В отличие от CSS, распределение значений слева, сверху, справа, снизу. Если указаны только два значения, это слева направо, сверху вниз. Вы можете использовать инструмент выбора в Fuse Studio, чтобы визуализировать примененные поля.

Далее мы добавляем фоновое изображение для страницы. Это принимает путь файла к фоновому изображению, которое вы хотите использовать. StretchMode of Fill заставляет фон растягиваться, чтобы заполнить весь экран:

1
<ImageFill File=»assets/images/seigaiha.png» StretchMode=»Fill» />

Вы можете скачать фоновое изображение, которое я использовал из репозитория GitHub . Или вы можете найти похожие шаблоны на сайте Toptal .

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

1
2
<Text Value=»HIIT Stopwatch» Color=»#333″ FontSize=»18″ Alignment=»Center» Margin=»0,0,0,10″ />
<Text FontSize=»65″ Font=»Thin» TextAlignment=»Center» Margin=»0,0,0,20″>{time_elapsed}</Text>

Далее, мы используем компонент <IconBtn> который мы создали ранее, — в отличие от веб-среды, в которой мы используем идентификатор шрифта. В Fuse вы должны использовать Unicode, назначенный шрифту значка, который вы хотите использовать. Вам также необходимо использовать &#x в качестве префикса. Когда эта кнопка нажата (называется Clicked ), addLap() функция addLap() объявленная в файле JavaScript:

1
<IconBtn Text=»&#xf2f1;»

В Font Awesome вы можете найти юникод шрифта иконки на его собственной странице .

Справа от кнопки добавления нового круга есть текст, который указывает, что кнопка выше предназначена для добавления новых кругов:

1
<Text Value=»Lap» Color=»#333″ FontSize=»15″ Alignment=»Center» Margin=»0,5,0,20″ />

Далее покажите кнопку для запуска и остановки таймера. Это также выполняет функцию, которую мы объявим позже:

1
<ToggleBtn Text=»{toggle_btn_text}» Clicked=»{toggle}» />

Далее нам нужно вывести круги, добавленные пользователем. Это включает в себя номер круга, пройденное расстояние и затраченное время. Элемент <Each> позволяет нам перебирать коллекцию объектов и отображать отдельные свойства для каждого объекта:

1
2
3
4
5
6
7
8
9
<StackPanel Margin=»20,40″>
    <Each Items=»{laps}»>
        <DockPanel Margin=»0,0,0,15″>
            <Text Alignment=»Left» FontSize=»18″ Color=»#333″ Value=»{title}» />
            <Text Alignment=»Center» FontSize=»18″ Color=»#333″ Value=»{distance}» />
            <Text Alignment=»Right» FontSize=»18″ Color=»#333″ Value=»{time}» />
        </DockPanel>
    </Each>
</StackPanel>

В приведенном выше коде мы используем <DockPanel> чтобы обернуть содержимое для каждого элемента. Этот тип панели позволяет нам «закреплять» его дочерние элементы с разных сторон (сверху, слева, справа и снизу) доступного пространства. По умолчанию это размещает его дочерние элементы непосредственно друг над другом. Чтобы равномерно распределить их, вам нужно добавить свойство Alignment .

Теперь мы готовы добавить код JavaScript. В Fuse JavaScript в основном используется для бизнес-логики и работы с собственными функциональными возможностями устройства. Эффекты, переходы и анимации для взаимодействия с пользовательским интерфейсом уже обрабатываются UX-разметкой.

Начните с импорта всех API, которые нам нужны. Это включает в себя Observable , который в основном используется для назначения переменных в пользовательском интерфейсе. Эти переменные могут быть обновлены с помощью JavaScript. Timer является эквивалентом функций setTimeout и setInterval в веб-версии JavaScript. GeoLocation позволяет нам получить текущее местоположение пользователя:

1
2
3
var Observable = require(«FuseJS/Observable»);
var Timer = require(«FuseJS/Timer»);
var GeoLocation = require(«FuseJS/GeoLocation»);

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

1
2
3
4
var time_elapsed = Observable();
var toggle_btn_text = Observable();
var is_running = Observable();
var laps = Observable();

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

1
2
toggle_btn_text.value = ‘Start’;
time_elapsed.value = «00:00:00»;

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

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

1
2
3
var time = 0;
var lap_time = 0;
var locations = [];

Функция toggle() используется для запуска и остановки таймера. Когда таймер в данный момент остановлен и пользователь нажимает на кнопку переключения, это единственный раз, когда мы сбрасываем значения для таймера и кругов. Это потому, что мы хотим, чтобы пользователь видел эти значения даже после того, как они остановили таймер.

После этого получите текущее местоположение пользователя и поместите его в массив locations . Это позволяет нам сравнить его со следующим местоположением позже, когда пользователь добавит круг. Затем создайте таймер, который выполняется каждые 10 миллисекунд. Мы увеличиваем общее time и время lap_time для каждого выполнения. Затем обновите пользовательский интерфейс с форматированным значением, используя formatTimer() :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function toggle(){
 
    if(toggle_btn_text.value == ‘Start’){ // the timer is currently stopped (alternatively, use is_running)
        laps.clear();
        time_elapsed.value = formatTimer(time);
        is_running.value = true;
 
        locations.push(GeoLocation.location);
 
        timer_id = Timer.create(function() {
            time += 1;
            lap_time += 1;
             
            time_elapsed.value = formatTimer(time);
        }, 10, true);
    }else{
        // next: add code for when the user stops the timer
    }
 
    toggle_btn_text.value = (toggle_btn_text.value == ‘Start’) ?
}

Когда пользователь останавливает таймер, мы удаляем его с помощью метода delete() в таймере. Для этого требуется timer_id который был возвращен при создании таймера:

1
2
3
4
5
Timer.delete(timer_id);
// reset the rest of the values
time = 0;
lap_time = 0;
is_running.value = false;

Далее идет функция форматирования таймера. Это работает путем преобразования миллисекунд в секунды и минуты. Мы уже знаем, что эта функция выполняется каждые 10 миллисекунд. И time увеличивается на 1 каждый раз, когда оно выполняется. Таким образом, чтобы получить миллисекунды, мы просто умножаем time на 10 . Оттуда мы просто вычисляем секунды и минуты на основе эквивалентного значения для каждой единицы измерения:

01
02
03
04
05
06
07
08
09
10
11
12
13
function formatTimer(time) {
    function pad(d) {
        return (d < 10) ?
    }
 
    var millis = time * 10;
    var seconds = millis / 1000;
 
    mins = Math.floor(seconds / 60);
    secs = Math.floor(seconds) % 60,
    hundredths = Math.floor((millis % 1000) / 10);
    return pad(mins) + «:» + pad(secs) + «:» + pad(hundredths);
}

Каждый раз, когда пользователь нажимает кнопку обновления, addLap() функция addLap() . Это добавляет новую запись поверх наблюдаемых laps :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function addLap() {
    if(time > 0){ // only execute when the timer is running
 
        lap_time_value = formatTimer(lap_time);
        lap_time = 0;
 
        var start_loc = locations[laps.length];
        var end_loc = GeoLocation.location;
        locations.push(end_loc);
 
        var distance = getDistanceFromLatLonInMeters(start_loc.latitude, start_loc.longitude, end_loc.latitude, end_loc.longitude);
         
        // add the new item on top
        laps.insertAt(0, {
            title: («Lap » + (laps.length + 1)),
            time: lap_time_value,
            distance: distance.toString() + » m.»
        });
         
    }
}

Вот функция для получения пройденного расстояния в метрах. Это использует формулу Haversine :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
function getDistanceFromLatLonInMeters(lat1, lon1, lat2, lon2) {
    function deg2rad(deg) {
      return deg * (Math.PI/180)
    }
 
    var R = 6371;
    var dLat = deg2rad(lat2 — lat1);
    var dLon = deg2rad(lon2 — lon1);
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 — a));
    var d = (R * c) * 1000;
    return d;
}

Не забудьте экспортировать все наблюдаемые значения:

1
2
3
4
5
6
7
8
module.exports = {
    toggle: toggle,
    toggle_btn_text: toggle_btn_text,
    is_running: is_running,
    time_elapsed: time_elapsed,
    laps: laps,
    addLap: addLap
}

Чтобы облегчить задачу, Fuse не включает в себя все пакеты, которые он поддерживает по умолчанию. Для таких вещей, как геолокация и локальные уведомления, вы должны указать Fuse включить их при сборке приложения. Откройте StopWatch.unoproj в корневом каталоге вашего проекта и Fuse.GeoLocation в массив Packages :

1
2
3
4
5
«Packages»: [
    «Fuse»,
    «FuseJS»,
    «Fuse.GeoLocation» // add this
],

Это должно дать Fuse указание включать пакет Geolocation при создании приложения для предварительного просмотра или создания установщика.

Прежде чем вы сможете запустить приложение на вашем устройстве iOS, вам необходимо сначала добавить идентификатор пакета в приложение. Откройте секундомер. unoproj файл и добавьте следующее под iOS . Это будет уникальный идентификатор для приложения, когда оно отправляется в магазин приложений:

1
2
3
4
5
6
7
«Packages»: [
    // …
],
«iOS»: {
    «BundleIdentifier»: «com.yourname.stopwatch»,
    «PreviewBundleIdentifier»: «com.yourname.stopwatch.preview»
}

Затем, в Xcode, войдите в систему под своей учетной записью разработчика Apple. Если у вас его еще нет, вы можете зайти на сайт разработчика Apple и создать его. Это действительно бесплатно для разработки и тестирования приложений на вашем устройстве iOS. Однако есть некоторые ограничения, если вы не являетесь частью программы разработчика.

Как только ваша учетная запись будет создана, перейдите в настройки Xcode и добавьте свою учетную запись Apple. Затем нажмите « Управление сертификатами» и добавьте новый сертификат для разработки под iOS. Этот сертификат используется для обеспечения того, чтобы приложение было из известного источника.

Как только это будет сделано, вы сможете запустить приложение на своем устройстве. Нажмите на Preview > Preview на iOS в Fuse Studio и подождите, пока он запустит Xcode. Как только Xcode открыт, выберите ваше устройство и нажмите кнопку воспроизведения. Это создаст приложение и установит его на ваше устройство. Если есть ошибка сборки, скорее всего, идентификатор пакета предварительного просмотра не уникален:

изменить идентификатор пакета

Изменение идентификатора пакета на что-то уникальное должно решить проблему. Как только ошибка в разделе подписи исчезнет, ​​нажмите кнопку воспроизведения еще раз, чтобы перестроить приложение. Это должно установить приложение на вашем устройстве.

Однако вы не сможете открыть приложение, пока не утвердите его. Это можно сделать на устройстве iOS, выбрав « Настройки» > « Основные» > « Управление устройством» и выбрав адрес электронной почты, связанный с вашей учетной записью Apple Developer. Утвердите это, и это должно разблокировать приложение.

Для Android вы должны иметь возможность предварительного просмотра приложения без каких-либо дополнительных шагов.

Это оно! В этом руководстве вы узнали основы создания приложения с использованием инфраструктуры Fuse. В частности, вы создали приложение для секундомера. Создав это приложение, вы узнали, как работать с UX-разметкой Fuse и несколькими API-интерфейсами JavaScript Fuse. Вы также узнали, как использовать Fuse Studio для предварительного просмотра приложения на вашем компьютере и телефоне во время его разработки.