В этом руководстве вы создадите мобильное приложение для службы обмена изображениями Imgur с использованием NativeScript. Я предполагаю, что это ваше первое приложение NativeScript, поэтому начнем с основ.
Вот как будет выглядеть финальное приложение:
Предпосылки
NativeScript включает в себя все основные операционные системы, поэтому перейдите по соответствующей ссылке, чтобы получить настройки:
Примечание . В Linux и Windows вы можете развертывать только на Android.
Создание нового проекта
Окончательный код этого урока можно найти на GitHub .
После завершения установки используйте команду tns create
для создания нового проекта:
tns create imgurclone --appid "com.yourname.imgurclone"
Команда tns create
принимает как минимум один аргумент и один параметр. Аргумент — это имя приложения ( imgurclone
), а опцией — идентификатор приложения ( com.yourname.imgurclone
).
Затем добавьте платформу, которую вы хотите развернуть. В этом случае Android.
cd imgurclone tns platform add android
Если вы используете OS X, вы можете добавить iOS в качестве платформы:
tns platform add ios
Выполнение команды tns platform add
создаст папку android или ios в каталоге платформы . Эти каталоги содержат файлы для создания приложения для этой конкретной платформы.
Сборка приложения
Теперь вы готовы к созданию приложения. В NativeScript вы в основном работаете в каталоге приложения . Он содержит весь код приложения, переведенный NativeScript на родной язык платформы (платформ).
По умолчанию каталог приложения содержит несколько файлов. Это исходные файлы для демонстрационного приложения NativeScript. Если вы новичок, я рекомендую вам попробовать запустить демонстрационное приложение на вашем устройстве или эмулятор Genymotion, чтобы почувствовать приложение, созданное с помощью NativeScript. Сделайте это, выполнив следующее:
tns run android
Как только вы закончите работу с демонстрационным приложением, удалите все файлы в каталоге приложения, кроме папки App_Resources . В этой папке хранятся значки приложений, заставки, звуковые файлы и другие ресурсы.
Файл точки входа
Файл app.js — это файл точки входа, используемый NativeScript. Вот где NativeScript смотрит, когда приложение компилируется. Добавьте следующее:
var application = require("application"); application.mainModule = "views/main/main"; application.cssFile = "./app.css"; application.start();
Разбить этот код. Сначала требуется модуль приложения . Это один из основных модулей, предоставляемых NativeScript. Эти модули обеспечивают абстракцию над реализациями, специфичными для платформы. Думайте об этом модуле как о клее, который удерживает все вместе.
var application = require("application");
Теперь присоедините основные файлы CSS и JavaScript, используемые приложением, и запустите его.
application.mainModule = "views/main/main"; //main javascript application.cssFile = "./app.css"; //main css application.start(); //start the app
views / main / main — это основной файл JavaScript для приложения, хранящийся в каталоге views / main, который создает полный путь views / main / main.js. Расширение файла .js опущено, поскольку NativeScript предполагает, что это всегда будет файл JavaScript.
./app.css является основной таблицей стилей для приложения. Любые включенные здесь стили будут влиять на все страницы в вашем приложении. Думайте об этом как о глобальной таблице стилей. Этот файл находится в том же каталоге, что и основной файл JavaScript, поэтому можно использовать ./
.
Примечание : app.css пуст для этого приложения, так как нет каких-либо общих стилей, которые я хотел бы добавить. Вы можете, конечно, добавить свой собственный.
Главная страница
Создайте каталог views / main и под ним создайте следующие файлы:
- main.xml : пользовательский интерфейс страницы
- main.js : файл JavaScript страницы
- main.css : CSS-файл страницы.
Так будет выглядеть каждая страница приложения. Если вы создали новую страницу, вы должны создать другую папку в каталоге views, а затем создать файлы xml, JavaScript и css.
Примечание . Каждый из файлов должен иметь то же имя, что и папка.
Главная страница XML
NativeScript использует XML-файлы для пользовательского интерфейса приложения, так что в этом есть большая кривая обучения по сравнению с Cordova, которая использует HTML для создания пользовательского интерфейса. Чтобы узнать больше о предоставляемых элементах пользовательского интерфейса, прочитайте страницу виджетов интерфейса пользователя NativeScript .
Добавьте следующее в файл main.xml :
<Page loaded="pageLoaded"> <StackLayout> <ActivityIndicator id="spinner" visibility="{{ busy ? 'visible' : 'collapse' }}" /> <ListView items="{{ topics }}" itemTap="openTopic"> <ListView.itemTemplate> <StackLayout class="topic"> <Label text="{{ name }}" class="topic-title" /> <Label text="{{ description }}" /> </StackLayout> </ListView.itemTemplate> </ListView> </StackLayout> </Page>
Разбить это. <Page>
является корневым элементом и указывает NativeScript создать новую страницу.
<Page loaded="pageLoaded"> ... </Page>
loaded
атрибут запускает функцию, когда страница полностью загружена. В этом случае функция pageLoaded
связана через файл main.js. Я объясню функцию pageLoaded
позже.
Далее идет тип макета, который вы хотите использовать, в данном случае StackLayout
. Это только один из многих макетов, которые вы можете использовать. Этот макет размещает все компоненты друг над другом, первый дочерний элемент вверху, а следующий ниже и т. Д.
<StackLayout> ... </StackLayout>
ActivityIndicator
отображает счетчик, показывающий, что приложение загружает данные из Imgur API в фоновом режиме. Атрибут id
будет использоваться в качестве ссылки для установки других атрибутов позже. Атрибут busy
принимает логическое значение, указывающее, извлекает ли приложение данные из API, и атрибут visibility
который принимает либо visible
либо collapse
значение. Если атрибут busy
равен true
тогда значение является visible
, если оно равно false
то collapse
(скрыто).
<ActivityIndicator id="spinner" visibility="{{ busy ? 'visible' : 'collapse' }}" />
Компонент ListView
, как следует из названия, создает список. Вы передаете атрибут items
, значением которого является имя переменной, содержащей данные, связанные через файл main.js. itemTap
атрибута itemTap
— это имя функции, выполняемой при itemTap
элемента в ListView
. Эта функция также была связана с файлом main.js.
<ListView items="{{ topics }}" itemTap="openTopic"> ... </ListView>
Возможно, вы заметили, что при связывании данных вы используете двойные фигурные скобки для переноса имени переменной, а при связывании функции вы просто включаете имя функции.
Внутри ListView
находится ListView.itemTemplate
который представляет каждый отдельный элемент в ListView
. Этот конкретный компонент повторяется в зависимости от того, сколько элементов было передано в ListView
. Внутри компонента снова используйте StackLayout
чтобы сложить две метки — название темы и описание.
Примечание. Двойные фигурные скобки используются снова, но вместо привязки данных они выводят данные.
<ListView.itemTemplate> <StackLayout class="topic"> <Label text="{{ name }}" class="topic-title" /> <Label text="{{ description }}" /> </StackLayout> </ListView.itemTemplate>
В NativeScript используйте компонент Label
для вывода текста.
Главная страница JavaScript
Далее идет файл JavaScript main.js или по умолчанию, который запускается при запуске приложения. Добавьте следующий код:
var api = require('../../lib/api'); var frame = require('ui/frame'); var topics = []; function pageLoaded(args){ var page = args.object; var spinner = page.getViewById('spinner'); spinner.busy = true; api.get('https://api.imgur.com/3/topics/defaults').then(function(json){ spinner.busy = false; topics = json.data; page.bindingContext = { topics: topics } }); } function openTopic(args){ var id = topics[args.index].id; var topmost = frame.topmost(); topmost.navigate({ moduleName: 'views/topic/topic', context: { id: id } }); } exports.pageLoaded = pageLoaded; exports.openTopic = openTopic;
Разбить этот код. Сначала включите все зависимости:
var api = require('../../lib/api'); var frame = require('ui/frame');
api
— это пользовательская библиотека, используемая для выполнения http-запросов к Imgur API. frame
это подмодули модуля пользовательского интерфейса, предоставляемые NativeScript. Это позволяет перемещаться между различными страницами приложения.
Затем создайте переменную, которая будет хранить текущие темы.
var topics = [];
Создайте функцию pageLoaded
которая выполняется при загрузке страницы. Ранее в файле main.xml вы называли эту функцию значением loaded
атрибута компонента Page
.
function pageLoaded(args){ var page = args.object; var spinner = page.getViewById('spinner'); spinner.busy = true; api.get('https://api.imgur.com/3/topics/defaults').then(function(json){ spinner.busy = false; topics = json.data; page.bindingContext = { topics: topics } }); }
Нарушение функции вниз. Сначала получите доступ к текущей странице, извлекая свойство object
из аргумента, переданного функции.
var page = args.object;
Он содержит метод getViewId
используемый для выбора компонентов на странице. В этом случае выберите спиннер и установите для его атрибута busy
значение true
чтобы он был видимым и вращался при загрузке страницы.
var spinner = page.getViewById('spinner'); spinner.busy = true;
Затем запросите все темы по умолчанию из Imgur API с помощью библиотеки api
импортированной ранее. Это предоставляет метод get
который позволяет вам указать URL-адрес для отправки запроса. Затем он возвращает обещание, поэтому возьмите данные ответа, предоставив функцию, выполняемую после возвращения обещания.
api.get('https://api.imgur.com/3/topics/defaults').then(function(json){ ... });
Внутри функции установите спиннер для остановки, назначьте данные ответа массиву topics
и затем привяжите их к текущей странице. На этом этапе компонент ListView
должен теперь иметь данные.
spinner.busy = false; //stop the spinner and hide it topics = json.data; //bind the topics to the current page page.bindingContext = { topics: topics }
Функция openTopic
перейти на страницу темы. Это значение было назначено ранее как значение для атрибута itemTap
в ListView
поэтому оно выполняется, когда пользователь нажимает на элемент в списке.
function openTopic(args){ var id = topics[args.index].id; //get the topic ID var topmost = frame.topmost(); //get the current page //navigate to the topic page and pass along the ID of the topic topmost.navigate({ moduleName: 'views/topic/topic', context: { id: id } }); }
Аргумент, передаваемый этой функции, содержит индекс текущего элемента, поэтому используйте значение индекса для доступа к идентификатору темы.
var id = topics[args.index].id; //get the topic ID
Затем получите текущую страницу, а затем перейдите к странице темы, вызвав метод navigate
. Это принимает объект, содержащий moduleName
и context
. Имя moduleName
— это путь к странице темы, т.е. views / topic / topic.js .
Примечание . Путь не относится к текущему файлу, поэтому всегда следует начинать с корня каталога приложения . context
— это объект, содержащий данные, которые вы хотите передать на следующую страницу. В этом случае только передача идентификатора темы.
var topmost = frame.topmost(); //get the current page //navigate to the topic page and pass along the ID of the topic topmost.navigate({ moduleName: 'views/topic/topic', context: { id: id } });
Наконец, сделайте функции pageLoaded
и openTopic
доступными для файла main.xml , экспортировав их.
exports.pageLoaded = pageLoaded; exports.openTopic = openTopic;
Главная страница стилей
Файл main.css содержит следующее:
.topic { padding: 10; } .topic-title { font-size: 20; font-weight: bold; }
Это простой CSS, но обратите внимание, что NativeScript поддерживает только подмножество CSS. Это означает, что не все функции CSS могут быть использованы. Например, вы не можете использовать поплавки или позиционирование. Найдите дополнительную информацию о конкретных свойствах, поддерживаемых на странице «Стиль» .
Страница темы
На странице темы отображаются случайные фотографии под выбранной в данный момент темой.
Теперь вы должны знать шаги, но если нет, то вот файлы, которые вам нужно создать:
- Тема / topic.xml
- тема / topic.js
- Тема / topic.css
Страница темы XML
Добавьте следующее в файл topic.xml :
<Page loaded="pageLoaded"> <Page.actionBar> <ActionBar title="imgurclone"> <NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="backToTopics"/> </ActionBar> </Page.actionBar> <StackLayout> <Button text="Pick Random Items" tap="pickRandomItems" /> <ActivityIndicator id="spinner" visibility="{{ busy ? 'visible' : 'collapse' }}" /> <ListView items="{{ photos }}" itemTap="viewImage"> <ListView.itemTemplate> <StackLayout class="photo-container"> <Image src="{{ 'http://i.imgur.com/' + id + 'm.jpg' }}" class="photo" stretch="aspectFit"/> <Label text="{{ title }}" textWrap="true" /> </StackLayout> </ListView.itemTemplate> </ListView> </StackLayout> </Page>
Разбить этот код. Сначала вы назначаете функцию, выполняемую при загрузке страницы. К настоящему времени вы должны знать, что это хорошее место для загрузки данных, необходимых странице.
<Page loaded="pageLoaded"> ... </Page>
Внутри Page
находится компонент ActionBar
. Это позволяет добавить кнопку для перехода обратно на главную страницу ( main / main.xml ).
<Page.actionBar> <ActionBar title="imgurclone"> <NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="backToTopics"/> </ActionBar> </Page.actionBar>
Прежде чем перейти к особенностям навигационной кнопки, обратите внимание на атрибут title
панели действий. Я указал imgurclone
который является названием приложения. Это не было необходимо ранее на главной странице, потому что NativeScript автоматически добавляет заголовок, содержащий заголовок приложения. Это означает, что ActionBar
заменяет заголовок по умолчанию, поэтому вам нужно указать заголовок самостоятельно.
После этого следующая кнопка NavigationButton
— это обычная кнопка без рамки. Атрибут text
указан, но на самом деле android.systemIcon
используется для отображения кнопки возврата системы Android по умолчанию. Далее идет функция tap
которая выполняется, когда пользователь нажимает на кнопку.
<NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="backToTopics"/>
Далее идет StackLayout
который содержит кнопку для выбора случайных элементов, сохраненных приложением, снова ActivityIndicator
и ListView
котором перечислены фотографии по выбранной теме.
<StackLayout> <Button text="Pick Random Items" tap="pickRandomItems" /> <ActivityIndicator id="spinner" visibility="{{ busy ? 'visible' : 'collapse' }}" /> <ListView items="{{ photos }}" itemTap="viewImage"> ... </ListView> </StackLayout>
Внутри ListView
отобразите фотографию и заголовок.
<StackLayout class="photo-container"> <Image src="{{ 'http://i.imgur.com/' + id + 'm.jpg' }}" class="photo" stretch="aspectFit"/> <Label text="{{ title }}" textWrap="true" /> </StackLayout>
В NativeScript вы отображаете изображения с помощью компонента Image
который принимает атрибут src
с использованием идентификатора.
Примечание : вместо .jpg используется m.jpg . М позволяет указать размер изображения, в данном случае средний. Это необходимо сделать, поскольку URL-адрес по умолчанию ссылается на полноразмерное изображение, которое слишком велико для телефона. Атрибут stretch
позволяет указать, как будет изменяться размер изображения в зависимости от высоты и ширины, указанных для пункта назначения (компонент Image
). aspectFit
означает, что размер изображения будет изменен в соответствии с назначением при сохранении соотношения сторон.
<Image src="{{ 'http://i.imgur.com/' + id + 'm.jpg' }}" class="photo" stretch="aspectFit"/>
Компонент Label
имеет новый атрибут textWrap
установленный в true
. Это необходимо, потому что этот атрибут по умолчанию имеет значение false
. Это означает, что текст будет усечен, если он занимает более одной строки. Значение true
позволяет избежать усечения и просто отображать полный текст независимо от того, сколько строк он потребляет.
<Label text="{{ title }}" textWrap="true" />
Страница темы JavaScript
Добавьте следующее в файл JavaScript страницы темы , topic / topc.js :
var _ = require('lodash'); var api = require('../../lib/api'); var frame = require('ui/frame'); var topmost = frame.topmost(); var unfiltered_photos = []; var page; var photos = []; function pageLoaded(args){ page = args.object; var topic_id = page.navigationContext.id; var spinner = page.getViewById('spinner'); spinner.busy = true; api.get('https://api.imgur.com/3/topics/' + topic_id + '/viral/1').then(function(json){ unfiltered_photos = json.data; photos = getPhotos(unfiltered_photos); page.bindingContext = { photos: photos } spinner.busy = false; }); } function backToTopics(){ topmost.goBack(); } function pickRandomItems(){ photos = getPhotos(unfiltered_photos); page.bindingContext = { photos: photos } } function getPhotos(unfiltered_photos){ var photos_to_show = 5; var filtered_photos = _.reject(unfiltered_photos, function(photo){ return photo.is_album; }); var random = _.random(0, filtered_photos.length - photos_to_show - 1); var photos = _.slice(filtered_photos, random, random + photos_to_show); return photos; } function viewImage(args){ var link = photos[args.index].link; topmost.navigate({ moduleName: 'views/image/image', context: { url: link } }); } exports.pageLoaded = pageLoaded; exports.backToTopics = backToTopics; exports.pickRandomItems = pickRandomItems; exports.viewImage = viewImage;
Взломать код Сначала включите необходимые модули:
var _ = require('lodash'); var api = require('../../lib/api'); var frame = require('ui/frame');
Здесь нет ничего нового, кроме lodash, который является утилитарной библиотекой для JavaScript. Установите его, используя npm:
npm install lodash --save
Сохраните ссылку на текущую страницу, она используется позже, чтобы вернуться к странице тем и перейти к странице изображения.
var topmost = frame.topmost();
Создайте переменные для хранения данных фотографии из Imgur api, текущей страницы и фотографий, отображаемых в данный момент.
var unfiltered_photos = []; //photo data from imgur api var page; //the current page var photos = []; //the photos that are currently displayed
Далее функция pageLoaded
.
function pageLoaded(args){ page = args.object; var topic_id = page.navigationContext.id; //get the ID that was passed by the main page. var spinner = page.getViewById('spinner'); spinner.busy = true; //fetch the photo data from the imgur api based on the selected topic api.get('https://api.imgur.com/3/topics/' + topic_id + '/viral/1').then(function(json){ unfiltered_photos = json.data; photos = getPhotos(unfiltered_photos); page.bindingContext = { photos: photos } spinner.busy = false; }); }
Опять же, ничего нового здесь, кроме получения идентификатора, переданного главной страницей:
var topic_id = page.navigationContext.id;
id
используется в качестве части URL-адреса для выполнения запроса. Внутри функции присвойте ответ, содержащий массив данных фотографии, на unfiltered_photos
. Затем передайте это функции getPhotos
, основной целью которой является фильтрация фотографий. Вы увидите, как это работает более подробно позже. Наконец, свяжите это с текущей страницей.
api.get('https://api.imgur.com/3/topics/' + topic_id + '/viral/1').then(function(json){ unfiltered_photos = json.data; photos = getPhotos(unfiltered_photos); page.bindingContext = { photos: photos } spinner.busy = false; });
Функция backToTopics
используется для возврата на предыдущую страницу (главную страницу).
function backToTopics(){ topmost.goBack(); }
pickRandomItems
вызывается каждый раз, когда пользователь нажимает кнопку выбора случайных элементов из данных текущей фотографии. Он вызывает функцию getPhotos
чтобы получить случайные данные фотографии, выбранные из текущего набора результатов, и устанавливает их на текущей странице.
function pickRandomItems(){ photos = getPhotos(unfiltered_photos); page.bindingContext = { photos: photos } }
Функция getPhotos
принимает необработанные данные о фотографии, полученные из Imgur API, а затем отклоняет все элементы с типом «альбома». Если вы заходили на веб-сайт Imgur раньше, вы могли видеть, что есть коллекция фотографий, называемая альбомами. Вам нужны только отдельные фотографии, поэтому исключите те, которые используют функцию reject
в lodash. Затем выберите случайное число, используемое в качестве начального индекса для выбора предметов для показа.
function getPhotos(unfiltered_photos){ //the number of photos to show in the list at a time var photos_to_show = 5; //reject all albums var filtered_photos = _.reject(unfiltered_photos, function(photo){ return photo.is_album; }); //pick random number var random = _.random(0, filtered_photos.length - photos_to_show - 1); //use the random number as a starting index for extracting the items to show var photos = _.slice(filtered_photos, random, random + photos_to_show); return photos; }
Функция viewImage
переходит на страницу, которая показывает фотографию, выбранную пользователем. NativeScript не поддерживает отображение анимированных (gif) изображений с использованием компонента Image
. На Github есть открытый вопрос об этом ограничении. Если вы читаете эту статью позже, она может быть решена. На данный момент текущим решением является использование веб-представления, которое будет отображать изображение.
Перейдите на страницу, которая использует веб-представление, только передав URL-адрес изображения на следующую страницу.
function viewImage(args){ var link = photos[args.index].link; topmost.navigate({ moduleName: 'views/image/image', context: { url: link } }); }
Выставляем все функции:
exports.pageLoaded = pageLoaded; exports.backToTopics = backToTopics; exports.pickRandomItems = pickRandomItems; exports.viewImage = viewImage;
Таблица стилей темы
Ранее вы задали класс photo
для компонента Image
и этот стиль устанавливает ширину и высоту фотографии. Это пункты назначения, о которых я упоминал ранее. Вам необходимо указать размеры, потому что это разрушит макет, если он не указан.
.photo-container { padding: 20; } .photo { width: 250; height: 250; }
Страница изображения
Страница изображения отображает изображение в веб-представлении, и вы уже должны знать детали ( подсказка : проверьте предыдущие две страницы приложения). Разница лишь в том, что вам не нужно создавать файл image.css, так как эта страница не нуждается в стилизации.
XML страницы изображения
Добавьте следующее в файл image.xml :
<Page loaded="pageLoaded"> <Page.actionBar> <ActionBar title="imgurclone"> <NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="backToTopic"/> </ActionBar> </Page.actionBar> <StackLayout> <WebView src="{{ imageUrl }}" /> </StackLayout> </Page>
Взломать код Включите заголовок, содержащий кнопку, которая позволяет пользователю вернуться на страницу темы.
<Page.actionBar> <ActionBar title="imgurclone"> <NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="backToTopic"/> </ActionBar> </Page.actionBar>
Затем добавьте компонент WebView
и укажите src
в качестве URL- WebView
изображения.
<StackLayout> <WebView src="{{ imageUrl }}" /> </StackLayout>
Страница изображения JavaScript
Файл JavaScript для страницы изображения содержит код для получения URL-адреса изображения и возврата на страницу темы.
var frame = require('ui/frame'); var topmost = frame.topmost(); function pageLoaded(args){ //get image url passed from the previous page var page = args.object; var url = page.navigationContext.url; page.bindingContext = { imageUrl: url } } //go back to the topic page function backToTopic(){ topmost.goBack(); } //expose the functions exports.pageLoaded = pageLoaded; exports.backToTopic = backToTopic;
API
Создайте lib / api.js , который является пользовательской библиотекой для отправки запросов к Imgur API. Это использует функцию fetch
встроенную в NativeScript. Он принимает URL-адрес, по которому вы хотите сделать запрос, в качестве первого аргумента и объект, содержащий параметры для передачи в запрос, в качестве второго. Такие параметры, как заголовок, тело запроса и метод запроса. Для запроса Imgur API не требуется создавать приложение на Imgur, я показываю его в демонстрационных целях.
Метод fetch
возвращает обещание, поэтому включите метод then
и передайте функцию, которая вызовет метод json
в ответе для получения объекта JavaScript. Это снова возвращает обещание, поэтому подключите другой метод then
для сбора данных ответа.
Примечание . Результатом метода fetch
является обещание, и поэтому вызов api.get
ранее соединяется с другим методом then
для получения фактических данных.
exports.get = function(url){ return fetch( url, { 'headers': {'Authorization': 'Client-ID xxxxxxxxxxx'} } ).then(function(response){ return response.json(); }).then(function(json){ return json; }); }
Запуск проекта
Чтобы запустить приложение на устройстве, используйте команду tns run
за которой следует имя платформы:
tns run android
Если вы хотите проверить изменения, вы можете использовать команду tns livesync
. Это обновляет приложение в устройстве каждый раз, когда вы нажимаете сохранить.
tns livesync android --watch
Вывод
Это оно! Из этого урока вы узнали, как работать с NativeScript, создавая приложение, которое взаимодействует с imgur API для получения фотоданных. Вы использовали такие компоненты, как изображение и веб-просмотр, а также способ запуска приложения на устройстве.