Вы зашли как: Гость
Опрос
Верите ли вы в скорый выход Surface Andromeda?

Начала Metro-программирования: создание интернет-приложений (ч.2)

Напечатать страницу
26.07.2012 13:45 | dronov_va

Окончание статьи, посвящённой программированию Metro-приложений, работающих с Интернетом.


5. Вывод веб-страниц. Фреймы
Осталось только выяснить, каким образом можно вывести на экран содержимое веб-страницы с указанным интернет-адресом. Это нам потребуется для вывода полного текста выбранной пользователем новости.

Для этого предназначены фреймы HTML. Фрейм — это особый элемент интерфейса, который можно рассматривать как веб-обозреватель, встроенный прямо в Metro-приложение. Мы можем открыть во фрейме любую веб-страницу, просто указав её интернет-адрес.

Фрейм создаётся с помощью парного тега <iframe>. Содержимое у этого тега никогда не указывается.

<iframe id="frmMain"></iframe>


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

<iframe id="frmMain" src="http://www.microsoft.com/"></iframe>


Фрейм представляется объектом HTMLIFrameElement. Этот объект поддерживает свойство src, соответствующее одноимённому атрибуту тега <iframe>.

var frmMain = document.getElementById("frmMain");
frmMain.src = "http://www.thevista.ru/";


Открываем в нашем фрейме другую веб-страницу.

Событие load возникает во фрейме сразу же после окончания загрузки веб-страницы.

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

Давайте сразу поместим в код интерфейса приложения - просмотровщика каналов новостей фрейм, в котором будем выводить полное содержимое новости. Для этого отыщем код, создающий блок divContent, и исправим его следующим образом:

<div id="divContent">
  <iframe id="frmContent"></iframe>
</div>


Вновь созданный фрейм с именем frmContent будет находиться в данном блоке.

Далее откроем файл default.css и создадим в нём новый стиль:

#frmContent {
  width: 100%;
  height: 100%;
}


Он растянет фрейм на всё пространство блока.

Сохраним исправленные файлы.


6. Логика приложения просмотрщика каналов новостей
И, вооружённые новыми знаниями, создадим наконец логику приложения просмотрщика каналов новостей.

Переключимся на файл default.js, где описывается логика приложения. Сразу же создадим код, объявляющий необходимые переменные:

var hHeader, ctrList, ctrURL, txtURL, frmContent, btnSubscribe, btnRefresh,
ctrAppBar, oFeedURL = null, arrFeed, dsrFeed;


Переменная oFeedURL будет хранить экземпляр объекта Windows.Foundation.Uri, представляющий интернет-адрес канала новостей, на который подписался пользователь. Сразу же присвоим ей значение null, говорящее, что подписка на канал новостей пока не выполнена. Переменная arrFeed будет хранить массив данных с новостями из канала, а переменная dstFeed — созданный на основе этого массива источник данных.

Создадим код инициализации:

document.addEventListener("DOMContentLoaded", function() {
  WinJS.UI.processAll().then(function() {
    hHeader = document.getElementById("hHeader");
    ctrList = document.getElementById("divList").winControl;
    ctrURL = document.getElementById("divURL").winControl;
    txtURL = document.getElementById("txtURL");
    ctrAppBar = document.getElementById("divAppBar").winControl;
    frmContent = document.getElementById("frmContent");
    btnSubscribe = ctrAppBar.getCommandById("btnSubscribe");
    btnRefresh = ctrAppBar.getCommandById("btnRefresh");
    var btnURL = document.getElementById("btnURL");
    btnRefresh.addEventListener("click", btnRefreshClick);
    btnURL.addEventListener("click", btnURLClick);
    ctrList.addEventListener("iteminvoked", ctrListItemInvoked);
    ctrAppBar.hideCommands([btnRefresh]);
  });
});


Помимо всего прочего, этот код изначально скроет кнопку Обновить панели инструментов, поскольку обновлять пока нечего.

Объявим функцию, которая будет выполнена после нажатия кнопки Подписаться всплывающего элемента:

function btnURLClick() {
  oFeedURL = new Windows.Foundation.Uri(txtURL.value);
  ctrURL.hide();
  ctrAppBar.showCommands([btnRefresh]);
  fillList();
}


Здесь мы получаем интернет-адрес, занесённый в поле ввода, создаем на его основе экземпляр объекта Windows.Foundation.Uri, присваиваем его переменной oFeedURL, убираем с экрана всплывающий элемент, выводим на экран кнопку Обновить и вызываем функцию fillList, которая загрузит содержимое канала и выведет его содержимое в списке.

Объявим эту функцию:

function fillList() {
  var oI, sAuthors, sContent;
  arrFeed = [];
  var oFeedClient = new Windows.Web.Syndication.SyndicationClient();
  oFeedClient.retrieveFeedAsync(oFeedURL).then(function(feed) {
    hHeader.textContent = feed.title.text;
    for (var i = 0; i < feed.items.size; i++) {
      oI = feed.items[i];
      sAuthors = "";
      for (var j = 0; j < oI.authors.size; j++) {
        if (sAuthors != "") {
          sAuthors += ", ";
        }
        sAuthors += oI.authors[j].name;
      }
      if (oI.content) {
        sContent = oI.content.text;
      } else {
        sContent = oI.summary.text;
      }
      arrFeed.push({
        title: oI.title.text,
        authors: sAuthors,
        content: sContent,
        url: oI.links[0].uri.absoluteUri,
        date: oI.publishedDate.getDate() + "." +
        (oI.publishedDate.getMonth() + 1) + "." +
        oI.publishedDate.getFullYear()
      });
    }
    dsrFeed = new WinJS.Binding.List(arrFeed);
    ctrList.itemDataSource = dsrFeed.dataSource;
  });
}


Объявим функцию, которая будет выполнена по нажатию на кнопке Обновить:

function btnRefreshClick() {
  fillList();
}


И объявим функцию, которая выполнится после выбора пункта в списке:

function ctrListItemInvoked(evt) {
  evt.detail.itemPromise.then(function(selected) {
    frmContent.src = selected.data.url;
  });
}


Она откроет во фрейме веб-страницу с полным содержимым выбранной в списке новости.

Сохраним все файлы и запустим приложение на выполнение. Подпишемся на какой-либо канал новостей и посмотрим, что отобразится в списке. Выберем в списке какую-либо новость и прочитаем её полное содержимое (рис. 1). Наконец, попробуем обновить содержимое канала.


Рис. 1. Интерфейс приложения просмотрщика каналов новостей FeedReader



7. Взаимодействие с удалёнными веб-сервисами
Но работа с каналами новостей - это не всё, чем может похвастаться платформа Metro. Она также предоставляет удобные инструменты для работы с удалёнными веб-сервисами - отправке им запросов и получение результатов их выполнения.

Возьмём, к примеру, поисковый сервис Microsoft Bing. Он позволяет исполнять запросы на поиск данных, отправленные сторонними приложениями, и возвращать результаты поиска в удобном формате. Проверим всё это в деле?

7.1. Формирование запроса
Сначала нам следует сформировать для удалённого веб-сервиса запрос на обработку данных, например, для их поиска. В составе этого запроса мы укажем входные параметры: ключевое слово для поиска, количество найденных позиций (например, интернет-адресов), которые будут возвращены в составе одного ответа, тип искомых позиций (веб-страницы, изображения или что-то ещё) и различные служебные сведения.

Большинство веб-сервисов принимают запросы, сформированные согласно методу GET. Он устанавливает следующие правила:

  • параметры запроса указываются прямо в составе интернет-адреса веб-сервиса;
  • параметры запроса и их значения записываются парами вида <имя параметра>=<значение параметра>;
  • отдельные пары "параметр — значение" отделяются друг от друга символами амперсанда (&);
  • параметры запроса отделяются от собственно интернет-адреса веб-сервиса символом вопросительного знака (?);
  • строковые значения параметров могут содержать только латинские буквы, цифры, символы подчёркивания и точки. Если в значении какого-либо параметра имеются другие символы, например, кириллические буквы или пробелы, они должны быть закодированы особым образом.


http://api.bing.net/json.aspx?Query=Metro&Sources=Image

Здесь мы сформировали согласно правилам метода GET запрос к веб-сервису Bing. Этот запрос включает два параметра:

  • Query - задаёт ключевое слово для поиска (в нашем случае - Metro);
  • Sources - задаёт тип искомых позиций (в нашем случае - Image, то есть графические изображения).


Для кодирования строковых значений параметров в подходящий для использования в запросе вид мы можем использовать встроенную JavaScript-функцию encodeURIComponent. В качестве единственного параметра она принимает строку и возвращает её в закодированном виде.

var sSource = "Метро";
var sEncoded = encodeURIComponent(sSource);
var sQuery = "http://api.bing.net/json.aspx?Query=" + sEncoded + "&Sources=Image"

ode]
Здесь мы включили в состав запроса закодированную строку "Метро".

Кстати, функция decodeURIComponent выполняет обратную задачу — преобразование закодированной строки в обычный вид.

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

7.2. Отправка запроса и получение ответа
Создав запрос, мы можем отправить его веб-сервису и получить результат его обработки. Для этого предназначен метод xhr объекта WinJS. (Объект WinJS предоставляет несколько служебных свойств и методов. Единственный его экземпляр создаётся платформой Metro и доступен из одноимённой переменной.)

В качестве единственного параметра метод xhr принимает экземпляр объекта Object, хранящий параметры запроса, которые задаются в различных его свойствах. Нас интересует свойство url, в котором указывается строка с полностью сформированным запросом.

Метод xhr возвращает обязательство. Для него мы в вызове метода then укажем функцию, которая выполнится после получения ответа от веб-сервиса. В качестве единственного параметра она получит экземпляр объекта, представляющий полученный ответ.

var oParams = {
  url: sQuery
};
WinJS.xhr(oParams).then(function(response) {
  //Ответ от веб-сервиса получен
  //Выполняем его обработку
});

полняем его обработку
});[/code]
7.3. Обработка ответа
Получить результаты обработки нашего запроса можно из свойства responseText данного объекта. Это свойство возвращает строку, представляющую собой JavaScript-код, который формирует экземпляр объекта Object, хранящий полученный результат.

Такой формат передачи данных по сети — в виде формирующего их JavaScript-кода — сейчас очень популярен. Он носит название JSON (JavaScript Object Notation, объектная нотация JavaScript).

Получив этот код, мы его выполним. Да-да, укажем платформе Metro, чтобы она его выполнила и выдала нам сам экземпляр объекта Object, что описывается этим кодом. Для этого мы используем метод parse объекта JSON. (JSON — один из встроенных в JavaScript объектов. Единственный его экземпляр создается платформой Metro и доступен из одноимённой переменной.)

Метод parse принимает в качестве единственного параметра строку с JavaScript-кодом и возвращает экземпляр объекта Object с данными.

[code]var oResponse = JSON.parse(response.responseText);[/code]
Набор свойств, поддерживаемых этим экземпляром объекта, зависит от конкретного веб-сервиса и описывается в его документации для разработчиков.

7.4. Введение в Bing API
Теперь кратко рассмотрим возможности веб-сервиса Microsoft Bing, предназначенные для разработчиков приложений, которые будут с ним взаимодействовать. Иначе говоря, его интерфейс разработки приложений, или API (Application Programming Interface). Конечно, целиком его мы разбирать не будем — для этого понадобится отдельная книга, — а познакомимся только с некоторыми его возможностями, теми, которые потом задействуем в нашей утилите, что будет искать изображения по заданному ключевому слову.

Внимание!
Любое приложение, которое будет работать с Microsoft Bing, обязано в составе каждого запроса указывать свой уникальный идентификатор, так называемый AppID. Его можно бесплатно получить на этой веб-странице.

7.4.1. Параметры запроса
Запрос, отправляемый Bing, должен включать следующие обязательные параметры:

  • Query — ключевое слово для поиска;
  • Sources — тип искомых данных. Для поиска изображений этот параметр должен иметь значение Image;
  • AppID — уникальный идентификатор приложения;
  • Image.Count — количество результатов, возвращаемых в составе одного ответа;
  • Image.Offset — номер первого результата, возвращаемого в составе данного ответа. Запомним, что нумерация возвращаемых результатов начинается с нуля.


Для получения ответа в формате JSON запрос следует посылать на интернет-адрес http://api.bing.net/json.aspx. (Bing также может отправлять ответы в формате XML; в этом случае запрос следует отправлять по другому интернет-адресу; за подробностями обращайтесь к документации по Bing API.)

http://api.bing.net/json.aspx?Query=Metro&Sources=Image&AppID=<идентификатор приложения>&Image.Count=10&Image.Offset=30

Этот запрос укажет Bing найти все изображения, связанные с ключевым словом "Metro", и вернуть в качестве ответа десять найденных изображений, начиная с 31-го.

7.4.2. Содержание ответа
Возвращённый Bing ответ после обработки, как уже говорилось ранее, представляет собой экземпляр объекта Object. Он имеет весьма сложную структуру и хранит множество данных.

Прежде всего, он содержит свойство SearchResponse, которое, собственно, и хранит данные ответа в виде экземпляра объекта Object. Этот экземпляр объекта, в свою очередь, содержит свойство Image, включающее сведения о найденных изображениях также в виде экземпляра объекта Object. Последний содержит три свойства:

  • Total — общее количество найденных изображений, включая и те, что не вошли в состав данного ответа, в виде целого числа. Отметим, что оно всегда вычисляется приблизительно.
  • Offset — порядковый номер первого изображения, входящего в состав данного ответа, из всех найденных. Указывается в виде целого числа.
  • Results — массив, каждый элемент которого представляет собой экземпляр объекта Object и хранит данные об одном найденном изображений. Фактически это уже знакомый нам по предыдущим статьям цикла массив данных.


[code]var iCount = oResponse.SearchResponse.Image.Total;
var arrResults = oResponse.SearchResponse.Image.Results;[/code]
Получаем общее количество найденных изображений и сами эти изображения.

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

  • Title — заголовок в виде строки;
  • Url — интернет-адрес веб-страницы, содержащей данное изображение, в виде строки;
  • MediaUrl — интернет-адрес файла, хранящего само изображение, в виде строки;
  • Width — ширина изображения в пикселах в виде целого числа;
  • Height — высота изображения в пикселах в виде целого числа;
  • Thumbnail — сведения о миниатюре изображения, сгенерированной самим Bing. Представляются в виде экземпляра объекта Object, имеющего следующие свойства:

    • Url — интернет-адрес файла с миниатюрой в виде строки:
    • Width — ширина миниатюры в пикселах в виде целого числа;
    • Height — высота миниатюры в пикселах в виде целого числа.


[code]var arrURLs = [];
var arrThumbnailURLs = [];
for (var i = 0; i < arrResults.length; i++) {
arrURLs.push(arrResults.Url);
arrThumbnailURLs.push(arrResults.Thumbnail.Url);
}[/code]
Помещаем интернет-адреса файлов с самими изображениями и их миниатюрами в массивы arrURLs и arrThumbnailURLs соответственно.

7.5. Утилита для поиска изображений в Интернете по ключевому слову
Настала пора проверить вновь полученные знания на практике. Создадим небольшую утилиту, которая, пользуясь услугами сервиса Bing, будет искать в Интернете изображения по заданному пользователем ключевому слову.

Сначала получим AppID для этого приложения. (Как это сделать, было рассказано ранее.) Когда будем его получать, зададим в качестве имени приложения ImageSearcher.

Далее запустим Visual Studio и создадим в нём новый проект ImageSearcher. Откроем файл default.html и впишем в тег <body> такой код:

[code]<div id="divListTemplate" data-win-control="WinJS.Binding.Template">
<div class="image-placement">
<img data-win-bind="src: thumbnailUrl" />
</div>
<div class="title-placement">
<p data-win-bind="textContent: title"></p>
</div>
</div>

<div id="divSearch">
<input type="text" id="txtKeyword" />
<input type="button" id="btnSearch" value="Искать" />
</div>

<div id="divList" data-win-control="WinJS.UI.ListView"
data-win-options="{layout: {type: WinJS.UI.ListLayout}, itemTemplate: divListTemplate, selectionMode: 'single'}"></div>

<div id="divViewer" data-win-control="WinJS.UI.ViewBox">
<img id="imgViewer" />
</div>

<div id="divNavigation">
<input type="button" id="btnPrevious" value="Предыдущие 10" disabled />
<input type="button" id="btnNext" value="Следующие 10" disabled />
</div>[/code]
Мы создали четыре блока, которые потом разместим на экране с применением сеточной разметки. Верхний блок включит в себя поле ввода для указания ключевого слова и кнопку Искать. Левый блок мы превратим в список Metro; он будет перечислять миниатюры и заголовки найденных изображений. Правый блок превратится в панель вывода Metro, включающую элемент графического изображения, в котором будет выводиться изображение, выбранное в списке. А нижний блок будет содержать кнопки Предыдущие 10 и Следующие 10; с помощью этих кнопок мы будем загружать предыдущую и следующую порцию найденных изображений.

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

Откроем файл default.css и создадим в нем стили, перечисленные далее.

[code]body {
display: -ms-grid;
-ms-grid-columns: 400px 1fr;
-ms-grid-rows: 40px 1fr 32px;
}
#divSearch {
-ms-grid-column-span: 2;
-ms-grid-column-align: end;
}
#divList {
-ms-grid-row: 2;
height: 100%;
}
#divViewer {
-ms-grid-row: 2;
-ms-grid-column: 2;
-ms-grid-column-align: center;
-ms-grid-row-align: center;
}
#divNavigation {
-ms-grid-row: 3;
-ms-grid-column-span: 2;
-ms-grid-column-align: end;
}
#divList .win-item {
height: 100px;
display: -ms-grid;
-ms-grid-columns: 180px 1fr;
-ms-grid-rows: 1fr;
}
.image-placement { width: 180px; }
.title-placement { -ms-grid-column: 2; }[/code]
Здесь нам давно всё знакомо. Мы создаём сеточную разметку и указываем для различных элементов интерфейса подходящие размеры.

Переключимся на файл default.js и сразу же введём код, создающий необходимые переменные:

[code]var txtKeyword, ctrList, imgViewer, btnPrevious, btnNext, sKeyword,
arrResults, dsrResults;[/code]
Объявим ещё четыре переменные:

[code]var sAppID = <идентификатор AppID нашего приложения>;
var iCurrent, iTotal;
var iResultCount = 10;[/code]
Понятно, что первая переменная будет хранить идентификатор AppID нашего приложения. Вторая сохранит порядковый номер первого изображения, загружаемого в составе текущего запроса, из всех найденных Bing. Третья сохранит общее количество найденных изображений, возвращённое Bing. А четвертая будет хранить количество изображений, которые Bing будет возвращать в состав одного ответа; зададим его равным 10.

Код инициализации будет следующим:

[code]document.addEventListener("DOMContentLoaded", function() {
WinJS.UI.processAll().then(function() {
txtKeyword = document.getElementById("txtKeyword");
var btnSearch = document.getElementById("btnSearch");
ctrList = document.getElementById("divList").winControl;
imgViewer = document.getElementById("imgViewer");
btnPrevious = document.getElementById("btnPrevious");
btnNext = document.getElementById("btnNext");

btnSearch.addEventListener("click", btnSearchClick);
ctrList.addEventListener("iteminvoked", ctrListItemInvoked);
btnPrevious.addEventListener("click", btnPreviousClick);
btnNext.addEventListener("click", btnNextClick);
});
});[/code]
Объявим функцию, которая запустит поиск изображений:

[code]function btnSearchClick() {
sKeyword = txtKeyword.value;
iCurrent = 0;
fillList();
}[/code]
Здесь мы сохраняем введенное пользователем ключевое слово в объявленной ранее переменной, указываем, чтобы изображения загружались, начиная с самого первого из найденных, и вызываем функцию fillList, которая запустит поиск и выведет его результаты в списке.

Объявим функцию fillList:

[code]function fillList() {
var oResponse, arrR;
var sQuery = "http://api.bing.net/json.aspx?Query=" +
encodeURIComponent(sKeyword) + "&Sources=Image&AppID=" + sAppID +
"&Image.Count=" + iResultCount + "&Image.Offset=" + iCurrent;
WinJS.xhr({url: sQuery}).then(function(response) {
arrResults = [];
oResponse = JSON.parse(response.responseText);
iTotal = oResponse.SearchResponse.Image.Total;
arrR = oResponse.SearchResponse.Image.Results;
for (var i = 0; i < arrR.length; i++) {
arrResults.push({
title: arrR.Title,
thumbnailUrl: arrR.Thumbnail.Url,
url: arrR.MediaUrl
});
}
dsrResults = new WinJS.Binding.List(arrResults);
ctrList.itemDataSource = dsrResults.dataSource;
btnPrevious.disabled = (iCurrent == 0);
btnNext.disabled = (iCurrent + iResultCount > iTotal);
});
}[/code]
Она отправит Bing запрос, получит ответ, сформирует на его основе источник данных и привяжет к нему список. Наконец, она будет делать кнопки Предыдущие 10 и Следующие 10 доступными или недоступными согласно текущему моменту.

Объявим функцию, которая выведет на экран изображение, соответствующее выбранному пользователем пункту списка:

[code]function ctrListItemInvoked(evt) {
evt.detail.itemPromise.then(function (selected) {
imgViewer.src = selected.data.url;
});
}[/code]
Наконец, объявим функции, которые будут загружать предыдущую и следующую порции найденных изображений:

[code]function btnPreviousClick() {
iCurrent -= iResultCount;
if (iCurrent < 0) {
iCurrent = 0;
}
fillList();
}
function btnNextClick() {
iCurrent += iResultCount;
fillList();
}[/code]
Сохраним все файлы и запустим приложение на выполнение. Введём в поле ввода какое-либо ключевое слово и запустим поиск. Когда список заполнится, выберем любой его пункт и посмотрим на изображение, что появится на экране (рис. 2). Попробуем перейти на следующую и предыдущую порцию найденных изображений. Напоследок выполним поиск по другому ключевому слову.


Рис. 2. Интерфейс утилиты для поиска изображений в Интернете ImageSearcher



8. Заключение
Мы рассмотрели базовые возможности платформы Metro по созданию интернет-приложений. Возможности исключительно актуальные, так как Metro-приложения создаются, в основном, для планшетов, которые предполагают активное использование Всемирной сети. (Точно так же, как сотовые телефоны рассчитаны на работу в сетях сотовой связи - иначе толку от них никакого.)

Конечно, это далеко не все интернет-возможности Metro. Данная платформа также позволяет выполнять асинхронную фоновую загрузку файлов с возможностью приостановки и возобновления и пересылку по сети данных на низком уровне - с применением сокетов TCP/IP. Наконец, Windows 8 предлагает поддержку технологии Proximity, позволяющей выполнять обмен произвольными данными при сближении устройств на определённое расстояние и задействующей для этого любые доступные аппаратные средства (WiFi-Direct, Bluetooth и др.). Но разговор обо всём выходит за рамки данной статьи.

Успешного Metro-программирования!


Дополнительные материалы



dronov_va, TheVista.Ru Team
Июль 2012

Комментарии

Комментариев нет...
Для возможности комментировать войдите в 1 клик через

По теме

Акции MSFT
105.9 0.00
Акции торгуются с 17:30 до 00:00 по Москве
Мы на Facebook
Мы ВКонтакте
Все права принадлежат © MSInsider.ru (ex TheVista.ru), 2017
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 2.34 (Страница создана за 0.067 секунд (Общее время SQL: 0.01 секунд - SQL запросов: 31 - Среднее время SQL: 0.00032 секунд))