Как я создаю своё первое мобильное приложение

Процесс шел с 24 октября по 19 ноября 2017 года, суть приложения — создание заметок, инструмент — Fuse, мой уровень знаний — базовое знание HTML/CSS

Меню

06.11.2017


7. Создание стикера: навигация

Текущая задача: сделать навигацию на окно создания стикера и возврат на страницу потока.

 

Вариант решения: выдвигать окно с помощью анимации
В анимационном прототипа я уже прикидывал какую анимацию хочу видеть: при клике на кнопку «New sticker» фоновая белая плашка должна растянуться на весь экран, после чего появляются элементы нового окна. При клике на отмену или «сохранить» — возвращаемся в поток обратной анимацией:

Для начала делаем цепочку событий «клик по кнопке — растягивание белой плашки». Это типичный пример анимации, когда воздействуя на один объект изменяются параметры другого:

Поэтому в шаблоне кнопки я могу прописать, чтобы она при клике меняла размер белой плашки. Однако это не подойдёт в моём случае, т.к. в UX Markup своя особенность анимации: она представляет собой отклонение от состояния «покоя»:

Так что как только клик закончится — плашка просто вернётся в своё обычное «спящее» состояние, а мне нужно как-бы «переключить» панель, чтобы она была видна.

Как тогда нам реализовать «всплытие» нового экрана после клика кнопки, если анимация не подходит?

 

Вариант решения: прописать через событие Event

У меня было предположение, что можно создать событие всплытия окна через тег Event. Например, можно прописать смену какого-то параметра панели, скажем, цвета после клика на объект:

<Panel ux:Name="panel" Color="Blue">
    <UserEvent Name="myEvent"/>
    <OnUserEvent EventName="myEvent">
        <Change panel.Color="Red" Duration="0.2" DurationBack="1" />
    </OnUserEvent>
    <Clicked>
        <RaiseUserEvent EventName="myEvent" />
    </Clicked>
</Panel>

Однако оказалось, что Event тоже работает как анимация, т.е. не остаётся в новом состоянии, а возвращается в исходное:

 

Вариант решения: использование навигационных объектов

В UX Markup есть несколько шаблонных и самых популярных навигационных объектов, которые и смогут помочь реализовать нужный функционал.
Один из них — PageControl, который позволяет запросто сделать слайд-навигацию:

<PageControl>
    <Panel Background="Red"/>
    <Panel Background="Blue"/>
</PageControl>

Но я ж стандартно не могу, поэтому и механизма у меня такого в приложении не предусмотрено, а свой, уникальный 🙂 Поэтому я буду использовать тег Navigator, который позволяет делать переходы между страницами со своей анимацией.

Сперва я делаю из текущих видимых элементов страницу (строка 2-14), затем добавляю страницу создания нового стикера (строка 15-23), публикую здесь только рассматриваемую сейчас часть кода, чтобы не загромождать:

<!-- Палитра слоёв (экземпляры) -->
<Page Name="Flow">
    <icon File="assets/flows.png" Alignment="TopLeft" />
    <icon File="assets/package.png" Alignment="TopRight" />
    <button_new button_name="New sticker" />
    <ScrollView>
        <StackPanel>
            <Each Items="{sticker_text}">
                <sticker sticker_text="{}"/>
            </Each>
        </StackPanel>
    </ScrollView>
    <Rectangle Color="#EAEAEA" />
</Page>
<Page Name="NewSticker" Color="#fff">
    <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
    <Button>
        <Text Alignment="TopRight" Margin="10" FontSize="24">Cancel</Text>
    </Button>
    <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB">
        <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
    </Button>
</Page>

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

<!-- Палитра слоёв (экземпляры) -->
<Router ux:Name="router" />
    <Navigator DefaultPath="Flow">
        <Page Name="Flow">
            <icon File="assets/flows.png" Alignment="TopLeft" />
            <icon File="assets/package.png" Alignment="TopRight" />
            <button_new button_name="New sticker" />
            <ScrollView>
                <StackPanel>
                    <Each Items="{sticker_text}">
                        <sticker sticker_text="{}"/>
                    </Each>
                </StackPanel>
            </ScrollView>
            <Rectangle Color="#EAEAEA" />
        </Page>
        <Page Name="NewSticker" Color="#fff">
            <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
            <Button>
                <Text Alignment="TopRight" Margin="10" FontSize="24">Cancel</Text>
            </Button>
            <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB">
                <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
            </Button>
        </Page>
    </Navigator>

Теперь у нас есть два экрана, один из которых скрыт, но навигация работать пока не будет, т.к. еще нет сигналов для переключения (команд).

Для этого я оберну объект button_new тегом Button для того, чтобы назначить ей свойство Clicked, т.к. сейчас это Panel, внешне похожая на кнопку, а у объекта Panel свойства Clicked нет:

<Router ux:Name="router" />
    <Navigator DefaultPath="Flow">
        <Page Name="Flow">
            <icon File="assets/flows.png" Alignment="TopLeft" />
            <icon File="assets/package.png" Alignment="TopRight" />
            <Button Clicked="{goto_newsticker}">
                <button_new button_name="New sticker" />
            </Button>            
            <ScrollView>
                <StackPanel>
                    <Each Items="{sticker_text}">
                        <sticker sticker_text="{}"/>
                    </Each>
                </StackPanel>
            </ScrollView>
            <Rectangle Color="#EAEAEA" />
        </Page>
        <Page Name="NewSticker" Color="#fff">
            <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
            <Button>
                <Text Alignment="TopRight" Margin="10" FontSize="24">Cancel</Text>
            </Button>
            <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB">
                <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
            </Button>
        </Page>
    </Navigator>

Clicked обеспечивает запуск команды, функцию которой я опишу в блоке js-кода: именно она и будет сообщать объекту Router, что надо перейти на другой экран. Поэтому Router можно воспринимать как регулировщик, джойстик нашего приложения, слушающий все команды, которые через него проходит, и «перелистывающий» экраны в после получения команд, например, goto(), push() или goBack().

Итак, прописываю команду перехода на другую страницу (функция goto_newsticker в js-коде, строка 3), объявляю её в тело программы (строка 6):

<JavaScript>
    var storage = ["Мой первый стикер", "Второй", "Третий", "Четвертый"];
    var goto_newsticker = function() { router.goto("NewSticker"); };
    module.exports = {
        sticker_text: storage,
        goto_newsticker: goto_newsticker
    }
</JavaScript>

Теперь после нажатия «New sticker» я буду попадать на экран создания нового стикера:

Остаётся только прописать аналогичные функции кнопкам отмены и сохранения (пока они просто работают как ссылки на экран потока стикеров) и поставить на каждую страницу свою собственную анимацию переходов между экранами:

<App>
<JavaScript>
    var storage = ["Мой первый стикер", "Второй", "Третий", "Четвертый"];
    var goto_newsticker = function() { router.goto("NewSticker"); };
    var goto_flow = function() { router.goto("Flow"); };
    module.exports = {
        sticker_text: storage,
        goto_newsticker: goto_newsticker,
        goto_flow: goto_flow
    }
</JavaScript>
<!-- Шаблоны (компоненты) -->
<Image ux:Class="icon" Width="30" Margin="10" Color="#C4C4C4" />
<Panel ux:Class="sticker" Width="220" Height="50" Margin="0,0,0,10" Padding="5,5,5,10" Color="#fff">  
    <string ux:Property="sticker_text" />
    <Text FontSize="16" Value="{ReadProperty sticker_text}" Color="#000" />
</Panel>
<Panel ux:Class="button_new" Alignment="BottomCenter" Color="#fff" Width="220" Height="78" >
    <string ux:Property="button_name" />
    <Text ux:Name="link" Value="{ReadProperty button_name}" FontSize="24" Color="#157EFB" Alignment="Center" />
    <WhilePressed>
        <Change this.Color="#157EFB" Duration="0.1" DurationBack="0.3" />
        <Change link.Color="#fff" Duration="0.4" DurationBack="0.3" />
    </WhilePressed>
</Panel>
<!-- Палитра слоёв (экземпляры) -->
<Router ux:Name="router" />
    <Navigator DefaultPath="Flow">
        <Page Name="Flow">
        <Transition>
            <Change Flow.Opacity="0.3" Duration="0.7" />
        </Transition>
            <icon File="assets/flows.png" Alignment="TopLeft" />
            <icon File="assets/package.png" Alignment="TopRight" />
            <Button Clicked="{goto_newsticker}">
                <button_new button_name="New sticker" />
            </Button>            
            <ScrollView>
                <StackPanel>
                    <Each Items="{sticker_text}">
                        <sticker sticker_text="{}"/>
                    </Each>
                </StackPanel>
            </ScrollView>
            <Rectangle Color="#EAEAEA" />
        </Page>
        <Page Name="NewSticker" Color="#fff">
        <Transition>
            <Scale Factor="0.7" Easing="ElasticIn" />
            <Move Y="1" RelativeTo="Size" Duration="0.4" Easing="ElasticOut" />
        </Transition>
            <Text Alignment="TopLeft" Margin="10" FontSize="24">New sticker</Text>
            <Button Clicked="{goto_flow}">
                <Text Alignment="TopRight" Margin="10" FontSize="24">Cancel</Text>
            </Button>
            <Button Alignment="Right" Width="120" Height="50" Margin="10" Color="#157EFB" Clicked="{goto_flow}">
                <Text Alignment="Center" Color="#fff" FontSize="24">Save</Text>
            </Button>
        </Page>
    </Navigator>
</App>

Наконец-то в приложении появилась навигационная система, которая координирует экраны и события:

Скачать архив проекта

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