Работа с RadSideDrawer и RadListView в мобильном приложении NativeScript Angular 2

Разработка /
Разработка: Работа с RadSideDrawer и RadListView в мобильном приложении NativeScript Angular 2

Сегодня мы рассмотрим пример включения бокового меню в приложение NativeScript под Android и iOS, построенное на Angular 2. Ну и чтобы два раза не вставать было интереснее, добавим в него мощный компонент построения списков RadListView.

Компоненты RadSideDrawer и RadListView доступны только в пакете NativeScript UI, который включает как бесплатную, так и платную версии. Всё, что мы рассмотрим в этом материале, доступно в бесплатной версии.

Для дальнейшей работы убедитесь в том, что у вас установлен NativeScript не ниже версии 2.5 и Android SDK и / или Xcode.
И если кто не в курсе, приложения под iOS можно разрабатывать только имея Mac.

Создадим чистый проект. Откройте командную строку и введите поочерёдно следующие команды:

tns create AwesomeProject --ng
cd AwesomeProject
tns platform add ios
tns platform add android

Более подробно об установке и настройке NativeScript читайте в официальной документации.

В нашем приложении будут использованы всплывающие сообщения. Установим плагин для этого:

tns plugin add nativescript-toast

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

Разработка: Работа с RadSideDrawer и RadListView в мобильном приложении NativeScript Angular 2

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

Теперь установим компоненты RadSideDrawer и RadListView. Выполните команду в терминале:

tns plugin add nativescript-telerik-ui

Более подробно об этом компоненте здесь.

После установки необходимо включить компоненты в приложение. Откроем файл app/app.module.ts и добавим в него такие строки:

import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { SIDEDRAWER_DIRECTIVES } from "nativescript-telerik-ui/sidedrawer/angular";
import { LISTVIEW_DIRECTIVES } from 'nativescript-telerik-ui/listview/angular';
 
import { AppComponent } from "./app.component";
 
@NgModule({
    declarations: [
        AppComponent,
        LISTVIEW_DIRECTIVES,
        SIDEDRAWER_DIRECTIVES
    ],
    bootstrap: [AppComponent],
    imports: [NativeScriptModule],
    schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }

Обратите внимание, мы импортировали две директивы NativeScript UI и добавили их в массив declarations блока @NgModule. Такие же действия потребуются в случае установки других компонентов пакета NativeScript UI.

Разработка бокового меню

Откроем файл app/app.component.ts и добавим в него такой код:

import { Component, ViewChild, OnInit } from "@angular/core";
import { ListViewEventData, RadListView } from "nativescript-telerik-ui/listview";
import { RadSideDrawerComponent, SideDrawerType } from "nativescript-telerik-ui/sidedrawer/angular";
import { View } from 'ui/core/view';
import * as Utils from "utils/utils";
import * as FrameModule from "ui/frame";
import * as Toast from 'nativescript-toast';
 
@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent implements OnInit {
 
    public emails: Array<string>;
    public selected: number;
    private drawer: SideDrawerType;
 
    @ViewChild(RadSideDrawerComponent)
    public drawerComponent: RadSideDrawerComponent;
 
    public constructor() {
        this.emails = [
            "Welcome to The Polyglot Developer Newsletter!",
            "Raspberry Pi Zero's Available!",
        ];
    }
 
    public ngOnInit() {
        this.drawer = this.drawerComponent.sideDrawer;
    }
 
    public onPullToRefreshInitiated(args: any) { }
 
    public onSwipeCellStarted(args: ListViewEventData) { }
 
    public onDelete() { }
 
    public onArchive() { }
 
    public onMenuTapped(value: any) {
        Toast.makeText(value + " menu item selected").show();
        this.drawer.closeDrawer();
    }
 
}

Здесь мы сделали следующее: в классе AppComponent ввели несколько общих и частных переменных. В переменной emails будет храниться список строк, которые мы выведем в RadListView. Переменная selected сообщит нам, какой элемент выбран в RadListView, чтобы мы могли запустить соответствующий метод. Переменная drawer будет ссылаться на компонент RadSideDrawer и мы сможем выполнять такие действия как открытие/закрытие меню.

По причине простоты реализации, мы инициируем строки, представляющие электронные адреса, в методе constructor. После вызова constructor будет вызван ngOnInit, где мы зададим @ViewChild равный переменной drawer.

Мы ещё не касались интерфейса, но при клике на элемент в боковом меню нам нужно будет выполнить метод. Метод onMenuTapped возьмёт любое значение и выведет во всплывающем сообщении. После показа сообщения, боковое меню закроется.
А теперь давайте построим интерфейс!

Откроем файл app/app.component.html и включим в него следующую разметку:

<ActionBar title="{N} UI Example"></ActionBar>
<RadSideDrawer>
    <StackLayout tkDrawerContent class="sideStackLayout">
        <StackLayout class="sideTitleStackLayout">
            <Label text="Menu"></Label>
        </StackLayout>
        <ScrollView>
            <StackLayout class="sideStackLayout">
                <Label text="Primary" class="sideLabel" (tap)="onMenuTapped('Primary')"></Label>
                <Label text="Social" class="sideLabel" (tap)="onMenuTapped('Social')"></Label>
                <Label text="Promotions" class="sideLabel" (tap)="onMenuTapped('Promotions')"></Label>
                <Label text="Labels" class="sideLabel" (tap)="onMenuTapped('Labels')"></Label>
                <Label text="Important" class="sideLabel" (tap)="onMenuTapped('Important')"></Label>
                <Label text="Starred" class="sideLabel" (tap)="onMenuTapped('Starred')"></Label>
                <Label text="Sent Mail" class="sideLabel" (tap)="onMenuTapped('Sent Mail')"></Label>
                <Label text="Drafts" class="sideLabel" (tap)="onMenuTapped('Drafts')"></Label>
            </StackLayout>
        </ScrollView>
    </StackLayout>
    <StackLayout tkMainContent class="mainContent">
        <GridLayout></GridLayout>
    </StackLayout>
</RadSideDrawer>

Не касаясь пока тегов class, у нас здесь есть и . У есть два очень важных компонента, заданных директивами tkDrawerContent и tkMainContent. Так мы отделяем часть меню от части главного окна приложения.

В tkDrawerContent (это наше боковое меню) у нас есть список компонентов . При клике на них будет вызван метод onMenuTapped, показано сообщение и меню закроется.

Довольно просто, не так ли?

Перейдём к главному представлению в секции tkMainContent.

Добавление многофункционального списка

Для начала вернёмся в логику на TypeScript, в которой мы оставили недописанными пару методов.
Откроем файл app/app.component.ts и добавим код:

public onPullToRefreshInitiated(args: any) {
    var radListView = args.object;
    setTimeout(() => {
        this.emails.push("NativeScript for the Angular Developer");
        radListView.notifyPullToRefreshFinished();
    }, 500);
}

Здесь в методе onPullToRefreshInitiated мы получаем ссылку на наш RadListView и настраиваем таймаут в полсекунды. Наши данные статичны и в таймауте нет необходимости, но они легко могут приходить и из базы данных, поэтому при срабатывании таймера мы увидим что мы хотели реализовать.

Реализовать свайпы в RadListView можно несколькими способами. Предположим, что мы хотим добавить кнопки действий. То есть, сдвинув элемент, мы увидим кнопки, на которые можно нажать.

public onSwipeCellStarted(args: ListViewEventData) {
    var swipeLimits = args.data.swipeLimits;
    swipeLimits.threshold = 60 * Utils.layout.getDisplayDensity();
    swipeLimits.left = 120 * Utils.layout.getDisplayDensity();
    swipeLimits.right = 120 * Utils.layout.getDisplayDensity();
    this.selected = args.itemIndex;
}

Здесь мы задали расстояние, на которое можно сдвинуть элемент в любую сторону, относительно размера экрана.

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

public onDelete() {
    let radListView = <RadListView> FrameModule.topmost().currentPage.getViewById("radlistview");
    Toast.makeText("Deleted").show();
    this.emails.splice(this.selected, 1);
    radListView.notifySwipeToExecuteFinished();
}

В методе onDelete мы получаем RadListView из интерфейса. Мы знаем позицию элемента, с которым работаем, поэтому можем её использовать для удаления элемента и возвратить свайп обратно. То же самое применимо к методу onArchive:

public onArchive() {
    let radListView = <RadListView> FrameModule.topmost().currentPage.getViewById("radlistview");
    Toast.makeText("Archived").show();
    this.emails.splice(this.selected, 1);
    radListView.notifySwipeToExecuteFinished();
}


Перейдём снова к интерфейсу. Откроем app/app.component.html и посмотрим на полный пример:

<ActionBar title="{N} UI Example"></ActionBar>
<RadSideDrawer tkExampleTitle tkToggleNavButton>
    <StackLayout tkDrawerContent class="sideStackLayout">
        <StackLayout class="sideTitleStackLayout">
            <Label text="Menu"></Label>
        </StackLayout>
        <ScrollView>
            <StackLayout class="sideStackLayout">
                <Label text="Primary" class="sideLabel" (tap)="onMenuTapped('Primary')"></Label>
                <Label text="Social" class="sideLabel" (tap)="onMenuTapped('Social')"></Label>
                <Label text="Promotions" class="sideLabel" (tap)="onMenuTapped('Promotions')"></Label>
                <Label text="Labels" class="sideLabel" (tap)="onMenuTapped('Labels')"></Label>
                <Label text="Important" class="sideLabel" (tap)="onMenuTapped('Important')"></Label>
                <Label text="Starred" class="sideLabel" (tap)="onMenuTapped('Starred')"></Label>
                <Label text="Sent Mail" class="sideLabel" (tap)="onMenuTapped('Sent Mail')"></Label>
                <Label text="Drafts" class="sideLabel" (tap)="onMenuTapped('Drafts')"></Label>
            </StackLayout>
        </ScrollView>
    </StackLayout>
    <StackLayout tkMainContent class="mainContent">
        <GridLayout>
            <RadListView
                id="radlistview"
                [items]="emails"
                itemSwipe="true"
                pullToRefresh="true"
                (pullToRefreshInitiated)="onPullToRefreshInitiated($event)"
                (itemSwipeProgressStarted)="onSwipeCellStarted($event)">
                <Template tkListItemTemplate let-email="item">
                    <StackLayout class="listItemStackLayout">
                        <Label text="{{ email }}"></Label>
                    </StackLayout>
                </Template>
                <GridLayout *tkListItemSwipeTemplate columns="auto, *, auto" class="listItemSwipeGridLayout">
                    <StackLayout class="archiveViewStackLayout" col="0" (tap)="onArchive()">
                        <Label text="ARCHIVE" verticalAlignment="center" horizontalAlignment="center"></Label>
                    </StackLayout>
                    <StackLayout class="deleteViewStackLayout" col="2" (tap)="onDelete()">
                        <Label text="DELETE" verticalAlignment="center" horizontalAlignment="center"></Label>
                    </StackLayout>
                </GridLayout>
            </RadListView>
        </GridLayout>
    </StackLayout>
</RadSideDrawer>


Внутри RadListView у нас есть атрибут id и методы onDelete и onArchive могут получить ссылку на нужный компонент. Список строится из нашего массива в переменной emails и здесь же перечислены привязки к методам логики в коде TypeScript.

Строки в пременной списка представлены компонентом в шаблоне списка.

<GridLayout *tkListItemSwipeTemplate columns="auto, *, auto" class="listItemSwipeGridLayout">
    <StackLayout class="archiveViewStackLayout" col="0" (tap)="onArchive()">
        <Label text="ARCHIVE" verticalAlignment="center" horizontalAlignment="center"></Label>
    </StackLayout>
    <StackLayout class="deleteViewStackLayout" col="2" (tap)="onDelete()">
        <Label text="DELETE" verticalAlignment="center" horizontalAlignment="center"></Label>
    </StackLayout>
</GridLayout>

tkListItemSwipeTemplate показывает, что это представление будет показано при свайпе. Это трёхколоночный макет сетки с кнопками по углам.

А теперь вернёмся к тегам class, упомянутым нами ранее, и рассмотрим стилизацию приложения.

Улучшения интерфейса с помощью CSS

Для простоты мы будем использовать только глобальные стили. Откройте файл app/app.css и добавьте следующее:

.sideStackLayout {
    background-color: #555555;
    color: #FFFFFF;
}
 
.sideTitleStackLayout {
    padding: 16;
    font-weight: bold;
    background-color: #333333;
}
 
.sideLabel {
    padding: 16;
}
 
.listItemStackLayout {
    padding: 16;
    background-color: #FFFFFF;
}
 
.archiveViewStackLayout {
    padding: 16;
    background-color: #387EF5;
    color: #FFFFFF;
}
 
.deleteViewStackLayout {
    padding: 16;
    background-color: #EF473A;
    color: #FFFFFF;
}
 
@import 'nativescript-theme-core/css/core.light.css';

Здесь нет ничего, кроме цвета фона и отступов, но наш интерфейс заметно преобразился.

Выполните команду tns run [platform] и будет собрано и запущено приложение для выбранной платформы.

По материалам «Use A Side Drawer And Feature Rich List View In A NativeScript Angular App» by Nic Raboy
0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.