Este artigo foi traduzido por máquina.

HTML5

Desenvolva aplicativos em HTML5 para o Windows Phone com o Apache Cordova

Colin Eberhardt

Baixar o exemplo de código

Este artigo apresenta o Apache Cordova, um quadro para a criação de aplicativos de plataformas móveis usando HTML5 e JavaScript e mostra como ele pode ser usado para desenvolver aplicativos para Windows Phone.

Sua plataforma de desenvolvimento nativo e Windows Phone permitem que você crie aplicativos Metro-estilo bonitos com facilidade. Com a recente parceria da Nokia, Windows Phone está começando a encontrar o seu caminho para bolsos mais e mais.

Dados recentes publicados pela investigação empresa Gartner Inc. prevê um futuro promissor para o ósmio de Microsoft (bit.ly/h5Ic32), com o significativo mercado compartilhar um mercado fragmentado. Se você estiver desenvolvendo um aplicativo smartphone, esta fragmentação do mercado significa que você que quer ter de escolher qual sistema operacional de destino ou gravar o mesmo aplicativo várias vezes usando a diversidade de línguas estes telefones requerem (c#, Java e Objective-C).

No entanto, não há outra maneira. Todos estes smartphones possui um navegador altamente capaz, de muitas maneiras mais capazes do que suas contrapartes desktop, onde algumas pessoas ainda usam navegadores arcaicos! Smartphones modernos permitem que você crie aplicativos que são executados dentro do navegador usando uma combinação de HTML5, JavaScript e CSS. Com essas tecnologias potencialmente você pode escrever um único aplicativo baseado em navegador que seja executado em uma variada gama de dispositivos smartphone.

Introdução Apache Cordova

Você pode criar um aplicativo móvel baseados em HTML5, criando uma página da Web pública com conteúdo JavaScript e HTML5 e direcionando as pessoas para a URL de hospedagem. No entanto, existem alguns problemas com essa abordagem. O primeiro é o modelo de distribuição através de lojas e mercados on-line. Você não pode enviar o URL que hospeda o aplicativo da Web para um mercado, então, como você pode monetize-lo? O segundo problema é como acessar hardware do telefone. Não há nenhum navegador amplamente com suporte APIs para acessar contatos do telefone, notificações, câmeras, sensores e assim por diante. Apache Cordova (apenas Córdova daqui em diante para abreviar) é um framework de código aberto que resolve esses dois problemas.

Córdoba começou a vida como PhoneGap, que foi desenvolvido pela Nitobi. Em outubro de 2011 Nitobi foi adquirida pela Adobe Systems Inc., com o quadro PhoneGap sendo open source sob a Apache Software Foundation e renomeada como Cordova. Essa transição é ainda em andamento.

Cordova fornece um ambiente para hospedar seu conteúdo HTML5/JavaScript dentro de um fino invólucro nativo. Para cada sistema operacional do smartphone, ele usa um Controlarar de navegador nativo para processar seu conteúdo do aplicativo, com os activos de aplicativo sendo agrupados no distribuível. Com Windows Phone, seus ativos HTML5 são empacotados no arquivo XAP e carregados no armazenamento isolado quando seu aplicativo Cordova é inicializado. Em tempo de execução, um Controlarar de WebBrowser processa seu conteúdo e executa o código JavaScript.

Cordova também fornece um conjunto de APIs padrão para acessar a funcionalidade que é comum em smartphones diferentes. Algumas destas funcionalidades incluem:

  • Eventos de ciclo de vida do aplicativo
  • Armazenamento (armazenamento local do HTML5 e bancos de dados)
  • Contatos
  • Câmera
  • Geolocation
  • Acelerômetro

Cada uma das anteriores funcionalidades é exposta como um Java­API de Script, que você usar em seu código JavaScript. Cordova faz todo o trabalho duro envolvido em fornecer a necessária implementação nativa, garantindo que você trabalhar contra as mesmas APIs JavaScript, independentemente do telefone OS seu código está sendo executado, como ilustrado na Figura 1.

Cordova Allows the Same HTML5 Application to Run Across a Range of Mobile OSes
Figura 1 Cordova permite que o aplicativo HTML5 mesmo executar toda uma gama de Mobile OSes

A maior parte deste artigo discute Cordova da perspectiva de um desenvolvedor de Windows Phone, onde o desenvolvimento ocorre dentro do Visual Studio e você testar seu aplicativo no emulador ou um dispositivo físico. Embora Cordova é uma tecnologia de plataforma cruzada, você normalmente desenvolve usando o editor ou o IDE da escolha, para que um desenvolvedor iOS iria desenvolver um aplicativo de Córdova no Xcode e um desenvolvedor Android provavelmente usaria Eclipse.

Cordova também tem um serviço de compilação baseados em nuvem chamado Build (build.phonegap.com), onde você pode enviar seu conteúdo de HTML5/JavaScript. Após um curto período de tempo ele retorna distribuições para a maioria das plataformas suportadas de Cordova. Isso significa que você não precisa ter cópias de vários idos de específico da plataforma (ou um computador Mac) para construir seu aplicativo para uma variedade de plataformas. O serviço de compilação é propriedade da Adobe e está atualmente em beta e livre para usar. Ele permanecerá livre para projetos open source.

Adquirindo as ferramentas

Presume-se que você já tem o Visual Studio, o Windows Phone SDK e (opcionalmente) Zune criada para desenvolvimento de Windows Phone. Se não, você pode obter as ferramentas gratuitamente por download Visual Studio 2010 Express para Windows Phone (bit.ly/dTsCH2).

Você pode obter as mais recentes ferramentas de desenvolvedor de Cordova do site do PhoneGap (phonegap.com), embora as versões futuras serão distribuídas através do Apache (incubator.apache.org/cordova). O download inclui os modelos, bibliotecas e scripts necessários para desenvolver aplicativos de Cordova em todas as plataformas suportadas. Você vai querer usar a versão de Windows Phone, é claro.

Depois de baixar as ferramentas de Cordova, siga o Windows Phone Get Started Guide (phonegap.com/start#wp) e instalar o modelo de Visual Studio. Criação de um "Hello World"-aplicação de estilo é tão simple como criar um novo projeto baseado no modelo fornecido, conforme mostrado na Figura 2.

Cordova for Windows Phone Includes a Visual Studio Template
Figura 2 Cordova para Windows Phone inclui um modelo de Visual Studio

Se você Compilação e implanta o projeto criado pelo modelo de seu emulador, você deve ser saudado com a mensagem "Olá Cordova," conforme mostrado na Figura 3.

The Cordova Template Application Running on an Emulator
Figura 3 modelo de Cordova aplicativo em execução em um emulador

A anatomia de um aplicativo de Cordova Windows Phone

Embora você pode desenvolver um aplicativo de Cordova sem muito conhecimento de como ele funciona sob o capô, vale a pena compreender quais são os vários arquivos gerados pelo modelo, como mostrado na Figura 4.

The Cordova Template Folder Structure
Figura 4 A estrutura de pasta do modelo de Cordova

Concentrando-se apenas nos arquivos Cordova em Figura 4 (de cima para baixo), observe o seguinte:

  • GapLib/WP7CordovaClassLib.dll é o assembly de Cordova. Isso contém a implementação nativa de Windows Phone das APIs de Cordova.
  • www é a pasta onde você coloca seus ativos de aplicativo, HTML5, JavaScript, CSS e imagens. O modelo gera um arquivo index. HTML básico e a folha de estilo master.css.
  • www/Cordova-1.5.0.js fornece o Membroscontinuarão Windows Phone­mentation das APIs JavaScript Cordova. Este interfaces com o código nativo contido em WP7CordovaClassLib.
  • BuildManifestProcessor.js é um arquivo de JavaScript que é chamado por uma etapa pós-compilação. Este arquivo gera a Cordova­SourceDictionary.xml arquivo, garantindo que qualquer coisa que você adiciona para a pasta www será carregada no armazenamento isolado.
  • CordovaSourceDictionary.xml é um arquivo XML gerado que lista todos os seus ativos de aplicativo. Quando seu aplicativo é iniciado pela primeira vez, este arquivo XML indica os arquivos a serem carregados no armazenamento isolado.

O arquivo MainPage.xaml contém uma instância do controle CordovaView, um Controlarar de usuário que contém um Controlarar de WebBrowser:

<Grid x:Name="LayoutRoot">
    <my:CordovaView Name="PGView" />
  </Grid>

Quando o aplicativo é iniciado, o controle de CordovaView se encarrega de carregar seus ativos de aplicativo no armazenamento local e navegando até o arquivo de www/index.html, assim, iniciar o aplicativo. Claro, você pode, colocar outros controles do Silverlight na página editando esse XAML, embora eu não o recomendaria. Se você estiver escrevendo um aplicativo de HTML5, sua intenção é provavelmente fazer este trabalho cruz-plataforma. Os controles que você adicionar a MainPage.xaml será, naturalmente, específicos para sua compilação Windows Phone.

Desenvolvimento de aplicações de Cordova

Você pode adicionar arquivos HTML, JavaScript e CSS para a pasta www e — enquanto você marcá-las com uma compilação ação de conteúdo — eles estarão incluídos em seu projeto e acessível via o Controlarar do navegador quando seu aplicativo é executado. Você pode usar qualquer uma das bibliotecas JavaScript/HTML5 padrão ou quadros em seu aplicativo de Cordova, enquanto eles são compatíveis com o navegador do telefone.

As APIs de Cordova estão documentadas no site Web de Cordova; Eu não descrevem em detalhes aqui. Uma coisa importante a notar é que você deve aguardar o evento de deviceready antes de fazer uso de qualquer um dos outros métodos de API. Se você inspecionar o arquivo index. HTML gerado a partir do modelo, você pode ver que ele espera até que o dispositivo está pronto antes de atualizar a interface do usuário:

<script type="text/javascript">
  document.addEventListener("deviceready",onDeviceReady,false);
  function onDeviceReady()
  {
    document.getElementById("welcomeMsg").innerHTML
      += "Cordova is ready!
version=" + window.device.cordova;
    console.log(
      "onDeviceReady.
You should see this " +
        "message in Visual Studio's output window.");
  }
</script>

O objeto console usado no código anterior permite que você Adicionar saída de Depurar seu aplicativo. Essas mensagens são enviadas ao console do Visual Studio por Cordova.

Uma página ou várias páginas aplicativo arquitetura

Ao criar aplicativos de Cordova, você pode empregar dois padrões distintos:

  • Aplicativos com várias páginas: Em aplicativos com várias páginas, múltiplas páginas HTML são usadas para representar as várias telas do seu aplicativo. Navegação entre páginas usa mecânica padrão do navegador, com links definidos pelas marcas de âncora. Cada página HTML inclui referências de script para o
  • O código JavaScript de Cordova e seu aplicativo JavaScript.
  • Aplicações de única página: Em aplicativos de página única, um único arquivo HTML referências Cordova e seu aplicativo JavaScript. Navegação entre as várias páginas do seu aplicativo é alcançada por atualizar dinamicamente o HTML processado. Da perspectiva do browser do telefone, a URL permanece o mesmo e não há nenhuma navegação entre as páginas.

A escolha que você faz entre esses dois padrões tem um impacto significativo sobre a estrutura do seu código.

De um modo geral, o padrão de várias páginas é mais adequado para aplicações que incluem principalmente conteúdo estático. Com essa abordagem, você pode tomar HTML/CSS/JavaScript que é atualmente usado em seu Web site e empacotá-lo, usando Cordova, para o telefone como um aplicativo. Mas a abordagem de várias páginas tem algumas desvantagens. Em primeiro lugar, quando o navegador navega de uma página para outra, tem de recarregar e analisar todo o JavaScript associado com a nova página. Há uma pausa perceptível como o ciclo de vida de Cordova, que cria o link entre as APIs JavaScript e c# homólogos, é executado. Em segundo lugar, porque o código JavaScript é serem recarregado, todos os Estado do aplicativo será perdido.

O padrão de página única supera os problemas associados com a abordagem de várias páginas. Cordova e aplicativo código JavaScript é carregado apenas uma vez, resultando em uma maior capacidade de resposta da interface do usuário e remover a necessidade de passar o estado do aplicativo de uma página para a próxima. A única desvantagem dessa abordagem é a complexidade adicional, com o código JavaScript sendo necessário para atualizar a interface do usuário quando a navegação ocorre.

O aplicativo de demonstração descrito neste artigo usa o padrão de página única. Para obter um exemplo da abordagem várias páginas em ação, eu recomendo olhar o projeto de DemoGAP CodePlex (demogap.codeplex.com), que fornece uma demonstração simples dos recursos da API de Cordova dentro de um aplicativo de Windows Phone.

O aplicativo de demonstração

O restante deste artigo descreve "Cordova Twitter Search," um simple aplicativo de Windows Phone que permite ao usuário pesquisar Twitter com base em uma ou mais palavras-chave, como mostrado na Figura 5.

The Cordova Twitter Search Demo Application
Figura 5 O aplicativo de demonstração de busca do Twitter de Cordova

Como Cordova, este aplicativo faz uso dos seguintes quadros:

  • jQuery e jQuery Templates: jQuery tornou-se o quadro padrão de facto para a manipulação do navegador Document Object Model (DOM). modelos de jQuery são plug-ins que a Microsoft desenvolveu (bit.ly/8ZO2V1), tornando mais fácil para criar modelos reutilizáveis de HTML que podem ser processados para o dom. Twitter que Search utiliza jQuery modelos para definir a interface do usuário para as várias páginas dentro do aplicativo.
  • Nocaute JS: Nocaute é um framework de Model-View-ViewModel (MVVM) que torna mais fácil construir ViewModels e mantê-los sincronizados com vistas de uma forma familiar para os desenvolvedores do Silverlight. É essa familiaridade que me levou a escolher nocaute sobre numerosos outros adequado JavaScript UI quadros.

Não vai ser abrangendo Knockout em detalhes neste artigo. Se você estiver interessado em aprender mais sobre este quadro, eu recomendo a leitura recente artigo do John Papa, "Começou da nocaute" (msdn.microsoft.com/magazine/hh781029). E se você não estiver familiarizado com o padrão MVVM (onde tem você sido escondido?), eu recomendo este excelente artigo de Josh Smith: "Aplicativos de WPF com o padrão de Design Model-View-ViewModel" (msdn.microsoft.com/magazine/dd419663).

Desenvolvendo aplicações JavaScript com o Visual Studio

Antes de investigar os detalhes do aplicativo, eu quero dizer algumas coisas sobre desenvolvimento de aplicativos JavaScript. Um dos desafios que o desenvolvedor do JavaScript é a natureza dinâmica da linguagem. Com JavaScript não são limitados por um sistema de tipo rígida; em vez disso, objetos podem ser criados dinamicamente. Isto constitui um desafio para os desenvolvedores do JavaScript editores e IDEs. Com rigidez linguagens como c# e Java, as informações do tipo podem ser usadas para fornecer navegação de código avançado, refatoração e IntelliSense. Por outro lado, a falta de informações do tipo com JavaScript, significa que o IDE fornece normalmente muito menos desenvolvedor SIDA.

Felizmente, as coisas melhoraram recentemente, com o Visual Studio 2010 executar pseudo-execution do seu código de JavaScript, a fim de determinar a "forma" de cada objeto, permitindo-lhe fornecer IntelliSense do JavaScript. Para tirar total proveito do suporte a IntelliSense, temos de fornecer o IDE com algumas "dicas" na forma de "referências" que informam ao IDE quais arquivos para incluir no seu pseudo-execution. Com o projeto de demonstração, todos os arquivos de começar com um comentário de referência que informa o IDE para incluir o arquivo de intellisense.js. O conteúdo deste arquivo é simplesmente uma lista de referências que asseguram que o IDE inclui todos os arquivos de JavaScript importantes de aplicativos, fornecendo suporte a IntelliSense qualidade em todo o aplicativo, conforme mostrado aqui:

/// Ensure IntelliSense includes all the files from this project.
///
/// <reference path="app.js" />
/// <reference path="viewModel/ApplicationViewModel.js" />
/// <reference path="viewModel/SearchResultsViewModel.js" />
/// <reference path="viewModel/TweetViewModel.js" />
/// <reference path="viewModel/TwitterSearchViewModel.js" />
/// <reference path="lib/jquery-1.6.4.js" />
/// <reference path="lib/cordova-1.5.0.js" />
/// <reference path="lib/knockout-1.2.1.js" />

JavaScript é uma linguagem descontraída e indulgente, com características tais como coerção de valor e semi­inserção de dois pontos, tornando-o fácil de usar em um ambiente de script. No entanto, esses mesmos recursos acabam sendo problemáticos ao gerenciar grandes quantidades de código. Por esse motivo eu altamente recomendo usar JSLint, uma ferramenta que se aplica a um conjunto mais rígido de codificação padrões para JavaScript. Uma extensão popular do Visual Studio adiciona suporte JSLint (jslint4vs2010.codeplex.com), relatando erros fiapos dentro do console de erro. Eu usei JSLint para o aplicativo de busca do Twitter (e praticamente qualquer outro projeto de JavaScript que eu trabalhei em).

Você poderá notar um comentário de "globais" no início de cada arquivo de JavaScript dentro do projeto. JSLint ajuda a evitar variáveis "vazamento" em escopo global por omissão acidental da palavra-chave var. O comentário de "globais" fornece JSLint com uma definição formal de que variáveis podem ocupar um escopo global.

A estrutura de aplicativo de MVVM

O Twitter Search é, do ponto de vista do Controlarar de navegador do telefone, um única página aplicativo. Por outro lado, da perspectiva do usuário, ele tem várias páginas, como pode ser visto em Figura 5. Para oferecer suporte a isso, um ViewModel Knockout é construído que contém uma pilha de instâncias de ViewModel, cada um representando uma página dentro do aplicativo. Como o usuário navega para uma nova página, o ViewModel correspondente é adicionado a esta pilha, e quando o usuário navega de volta, o ViewModel superior é retirado da pilha (ver Figura 6).

Figura 6 ApplicationViewModel Knockout

/// <reference path="..//intellisense.js" />
/*globals ko*/
function ApplicationViewModel() {
  /// <summary>
  /// The ViewModel that manages the ViewModel back-stack.
/// </summary>
  // --- properties
  this.viewModelBackStack = ko.observableArray();
  // --- functions
  this.
navigateTo = function (viewModel) {
    this.viewModelBackStack.push(viewModel);
  };
  this.back = function () {
    this.viewModelBackStack.pop();
  };
  this.templateSelector = function (viewModel) {
    return viewModel.template;
  }
}

Quando o aplicativo for iniciado, uma instância do ApplicationViewModel é criada e vinculada à interface do usuário usando o nocaute:

document.addEventListener("deviceready", initializeViewModel, false);
var application;
function initializeViewModel() {
  application = new ApplicationViewModel();
  ko.applyBindings(application);
}

A interface de usuário propriamente dito é bastante simple, constituído por um elemento de div que usa a vinculação de modelo vazado para renderizar a pilha de ViewModel:

<body>
  <h1>Cordova Twitter Search</h1>
  <div class="app"
    data-bind="template: {name: templateSelector,
                          foreach: viewModelBackStack}">
  </div>
</body>

A associação ao modelo Knockout funciona de maneira semelhante a ItemsControl Silverlight que vincula a uma matriz de instâncias de ViewModel e é responsável por gerar a exibição para cada um através de um modelo. Neste caso, a função templateSelector é chamada no ApplicationViewModel para determinar o modelo nomeado para cada ViewModel.

Se você executar este aplicativo, você encontrará que ele realmente não faz qualquer coisa — que é porque não há qualquer ViewModels para representar as páginas do aplicativo!

O TwitterSearchViewModel

Apresentarei um ViewModel primeiro, TwitterSearchViewModel, que representa a primeira página da aplicação. Este ViewModel expõe algumas simples observáveis propriedades que oferecem suporte a interface do usuário, nomeadamente a searchTerm, que é ligado para o campo de entrada de usuário, e isSearching, que é um booleano observável que desabilita o botão de busca quando as APIs do Twitter estão sendo consultadas através de HTTP. Ele também apresenta uma função de pesquisa que está ligada o botão de pesquisa, em grande parte da mesma maneira que você iria ligar um ICommand para um botão em Silverlight (consulte Figura 7).

Figura 7 A TwitterSearchViewModel

/// <reference path="..//intellisense.js" />
/*globals $ application ko localStorage SearchResultsViewModel TweetViewModel*/
function TwitterSearchViewModel() {
  /// <summary>
  /// A ViewModel for searching Twitter for a given term.
/// </summary>
  // --- properties
  this.template = "twitterSearchView";
  this.isSearching = ko.observable(false);
  this.searchTerm = ko.observable("");
  // --- public functions
  this.search = function () {
    /// <summary>
    /// Searches Twitter for the current search term.
/// </summary>
    // implementation detailed later in this article ...
};
}

A propriedade template do ViewModel nomes de Exibir que está associado este ViewModel. Esta vista é descrita como um modelo de jQuery dentro do arquivo index. html:

<script type=text/x-jquery-tmpl" charset="utf-8" id="twitterSearchView" 
  <div>
    <form data-bind="submit: search">
      <input type="text"
        data-bind="value: searchTerm, valueUpdate: 'afterkeydown'" />
      <button type="submit"
        data-bind="enable: searchTerm().length > 0 &&
          isSearching() == false">Go</button> 
    </form>     
  </div>
</script>

Se você adicionar uma instância do TwitterSearchViewModel para a pilha de ViewModel de aplicativo, o aplicativo agora mostra a primeira página, conforme mostrado na Figura 8.

The TwitterSearchViewModel Rendered via the twitterSearchView Template
Figura 8 O TwitterSearchViewModel processado através da twitterSearchView modelo

Criando um Metro UI com CSS

Uma das características mais marcantes do Windows Phone OS é a linguagem de design Metro, que orienta todos os aspectos da aparência do telefone. Esta linguagem de design, que favorece o conteúdo sobre cromo, não só é agradável aos olhos, também é prático, oferecendo interfaces altamente legíveis sobre o fator de forma pequeno telefone.

A interface do usuário atual, como mostrado na Figura 8, usa o estilo padrão do navegador e como resultado não é muito agradável aos olhos! Já existem alguns quadros bem estabelecidos para a criação de boa aparência UIs móveis usando HTML e CSS, tais como jQuery Mobile (jquerymobile.com). Actualmente, estas estruturas tendem a concentrar-se em imitar a aparência do iOS. Um aplicativo de Windows Phone Cordova poderia ser estilizado usando jQuery Mobile, embora ele provavelmente iria enfrentar alguns rejeição do usuário porque ele simplesmente não iria "encaixar" com a aparência geral do sistema operacional.

Felizmente, o cromo-free tema Metrô é realmente bastante fácil de replicar usando HTML e CSS. Na verdade, o Windows 8 trata HTML como um cidadão de primeira classe, permitindo que você desenvolva aplicações HTML5 Metro usando as APIs de tempo de execução do Windows.

Introduzindo as fontes corretas, tamanhos de fonte e cores através de algumas simples CSS (mostrado na Figura 9), você pode criar uma interface do usuário que acompanha de perto o tema de Metro, conforme mostrado na Figura 10.

Código de Figura 9 a seguir o tema de Metro

body
{
  background: #000 none repeat scroll 0 0;
  color: #ccc;
  font-family: Segoe WP, sans-serif;
}
h1
{
  font-weight: normal;
  font-size: 42.667px; /* PhoneFontSizeExtraLarge */
}
button
{
  background: black;
  color: white;
  border-color: white;
  border-style: solid;
  padding: 4px 10px;
  border-width: 3px; /* PhoneBorderThickness */
  font-size: 25.333px; /* PhoneFontSizeMediumLarge */
}
input[type="text"]
{
  width: 150px;
  height: 34px;
  padding: 4px;
}

The Twitter Search Application with a Metro CSS Style Applied
Figura 10 O aplicativo de busca do Twitter com um estilo CSS Metro aplicada

Um último aspecto que é um pouco mais de uma doação que esta é uma aplicação de HTML5 em vez de um nativo é que o usuário pode ainda "beliscar" a interface do usuário para torná-lo mais zoom. Isto pode ser parcialmente resolvido, adicionando a seguinte propriedade de meta para a página index. html:

<meta name="viewport" content="user-scalable=no" />

Isso informa o navegador que o usuário não é permitido para dimensionar o conteúdo processado. Infelizmente, a forma como isto é implementado pelo navegador Windows Phone permite que o usuário dimensionar o conteúdo, mas encaixa volta à escala original quando termina a interação. Isso não parece muito bom!

Eu descobri que, inspecionando a árvore visual do Controlarar Navegador da Web, é possível anexar manipuladores para os eventos de manipulação e proibi-los de subida até o TileHost nativo que processa o HTML5 conteúdo. Publiquei uma postagem no blog breves (bit.ly/vU2o1q) que inclui uma classe de utilitário simples que consegue isso. Mas você deve usar isso com cuidado, porque ele depende a estrutura interna do controle WebBrowser, que bem pode alteração nas futuras versões do sistema operacional Windows Phone.

Pesquisa Twitter

Se você olhar mais detalhadamente a função de pesquisa do TwitterSearchViewModel, ele consultará as APIs do Twitter por meio da função de "ajax" jQuery, que retorna uma resposta JSONP. Uma instância de TweetViewModel é construída a partir de cada um dos tweets retornados, e estes são usados para construir uma instância de SearchResultsViewModel (consulte Figura 11).

Figura 11 A função de pesquisa de TwitterSearchViewModel

this.search = function () {
  /// <summary>
  /// Searches Twitter for the current search term.
/// </summary>
  this.isSearching(true);
  var url = "http://search.twitter.com/search.json?q=" +
    encodeURIComponent(that.searchTerm());
  var that = this;
  $.ajax({
    dataType: "jsonp",
    url: url,
    success: function (response) {
      // Create an array to hold the results.
var tweetViewModels = [];
      // Add the new items.
$.each(response.results, function () {
        var tweet = new TweetViewModel(this);
        tweetViewModels.push(tweet);
      });
      // Navigate to the results ViewModel.
application.
navigateTo(new SearchResultsViewModel(tweetViewModels));
      that.isSearching(false);
    }
  });
};

O SearchResultsViewModel simplesmente contém uma lista de tweets:

/// <reference path="..//intellisense.js" />
/*globals ko*/
function SearchResultsViewModel(tweetViewModels) {
  /// <summary>
  /// A ViewModel that renders the results of a twitter search.
/// </summary>
  /// <param name="tweetViewModels">An array of TweetViewModel instances</param>
  // --- properties
  this.template = "searchResultsView";
  this.tweets = ko.observableArray(tweetViewModels);
}

E o TweetViewModel apresenta as propriedades de um chilrear individual e uma Selecione função que navega para o modo de exibição de cada tweet, como mostrado na Figura 12.

Figura 12 O TweetViewModel

/// <reference path="..//intellisense.js" />
/*globals application*/
function TweetViewModel(tweet) {
  /// <summary>
  /// A ViewModel that represents a single tweet
  /// </summary>
  /// <param name="tweet">A tweet as returned by the twitter search API</param>
  // --- properties
  this.template = "tweetDetailView";
  this.author = tweet.from_user;
  this.text = tweet.text;
  this.id = tweet.id;
  this.time = tweet.created_at;
  this.thumbnail = tweet.profile_image_url;
  // --- public functions
  this.select = function () {
    /// <summary>
    /// Selects this tweet, causing the application to navigate to a tweet-view.
/// </summary>
    application.
navigateTo(this);
  };
}

Novamente, os modelos que descrevem o modo de exibição para cada um destes ViewModels são adicionados ao arquivo index. html, conforme mostrado na Figura 13.

Figura 13 Adicionando modelos para o arquivo index. html

<script type=text/x-jquery-tmpl" charset="utf-8" id="searchResultsView">
  <div>
    <ul data-bind="template: {name: 'tweetView',
                              foreach: tweets}"> </ul>
  </div>
</script>
<script type="text/x-jquery-tmpl" charset="utf-8" id="tweetView">
  <li class="tweet"
      data-bind="click: select">
    <div class="thumbnailColumn">
      <img data-bind="attr: {src: thumbnail}"
                             class="thumbnail"/>
    </div>
    <div class="detailsColumn">
      <div class="author"
           data-bind="text: author"/>
      <div class="text"
           data-bind="text: text"/>
      <div class="time"
           data-bind="text: time"/>
    </div>
  </li>
</script>

Com esse código no lugar, quando o usuário pressiona o botão "ir" para pesquisar Twitter, um novo SearchResultsViewModel é adicionada à pilha de ViewModel. Isso resultará automaticamente no modelo searchResultsView está sendo processado dentro da div de "app". No entanto, porque a pilha de ViewModel é processada através de uma associação de modelo de foreach, isso não vai esconder as instâncias do modelo twitterSearchView; em vez deles vão ser empilhados uma sobre a outra. Isto pode ser resolvido através da adição de um par de regras CSS simples:

.app>div
{
  display: none;
}
.app>*:last-child
{
  display: block;
}

No primeiro seletor oculta todos os filhos imediatos da div marcado com a classe de aplicativo, enquanto o segundo seletor, que tem uma precedência mais alta, garante que o último filho é mostrado.

Com essas regras CSS no lugar, o aplicativo Twitter Search é totalmente funcional e navegável.

Gerenciando a pilha de volta

Com a busca do Twitter atual aplicativo você pode navegar de pesquisa página de resultados a um tweet individual, mas se você apertar o botão de trás do telefone o aplicativo sai imediatamente. Isso ocorre porque a navegação de aplicativo ocorre inteiramente dentro de um controle de navegador — assim, da perspectiva do quadro Silverlight, o aplicativo tem uma única página. Isso não só resulta em uma experiência de usuário ruim, ele quase certamente resultará na aplicação sendo rejeitada se apresentou para o mercado de Windows Phone.

Felizmente, a solução para este problema é simples. O ApplicationViewModel contém uma pilha de instâncias de ViewModel. Se houver mais de uma instância de ViewModel nesta pilha, você precisará lidar com o pressionamento de botão Voltar hardware e pop o ViewModel superior nesta pilha. Caso contrário, você pode permitir que o framework do Silverlight sair do aplicativo.

Para dar suporte a isso, um backButtonRequired dependente observável é adicionado ao ViewModel:

function ApplicationViewModel() {
  // --- properties
  this.viewModelBackStack = ko.observableArray();
  this.backButtonRequired = ko.dependentObservable(function () {   
    return this.viewModelBackStack().length > 1;
  }, this);
  // --- functions
  // ...
}

Quando o ViewModel é inicializado, você pode manipular as alterações para este observáveis e assinar os eventos ButtonBack que abastece de Cordova. Quando o evento for acionado, você chama a função voltar em ApplicationViewModel, que estala o ViewModel na extremidade superior da pilha. A associação ao modelo Knockout cuida de remover Vista associada da ViewModel de interface do usuário e o estilo CSS garante que agora é superior é visível (ver Figura 14).

Figura 14 manipulação pressionar um botão Voltar

function initializeViewModel() {
  application = new ApplicationViewModel();
  ko.applyBindings(application);
  // Handle the back button.
application.backButtonRequired.subscribe(function (backButtonRequired) {
    if (backButtonRequired) {
      document.addEventListener("backbutton", onBackButton, false);
    } else {
      document.removeEventListener("backbutton", onBackButton, false);
    }
  });
  var viewModel = new TwitterSearchViewModel();
  application.
navigateTo(viewModel);
}
function onBackButton() {
  application.back();
}

Porque o evento ButtonBack é fornecido através de Cordova, o código no Figura 14 vai funcionar muito bem se você executar o aplicativo usando um telefone diferente OS (contanto que o próprio telefone tem um botão Voltar hardware).

Persistência de Estado

Adicionaremos um recurso final para o aplicativo de busca do Twitter: Quando uma pesquisa retorna com êxito, o termo de pesquisa é adicionado a uma lista de pesquisas recentes (ver Figura 15).

Figura 15 adicionando um termo de pesquisa para uma lista de pesquisas recentes

function TwitterSearchViewModel() {
  /// <summary>
  /// A ViewModel for searching Twitter for a given term.
/// </summary>
  // --- properties
  // ...
some properties omitted for clarity ...
this.recentSearches = ko.observableArray();
  // --- functions
  // ...
some functions omitted for clarity ...
this.loadState = function () {
    /// <summary>
    /// Loads the persisted ViewModel state from local storage.
/// </summary>
    var state = localStorage.getItem("state");
    if (typeof (state) === 'string') {
      $.each(state.split(","), function (index, item) {
        if (item.trim() !== "") {
          that.recentSearches.push(item);
        }
      });
    }
  };
  function saveState() {
    /// <summary>
    /// Saves the ViewModel state to local storage.
/// </summary>
    localStorage.setItem("state", that.recentSearches().toString());
  }
  function addSearchTermToRecentSearches() {
    /// <summary>
    /// Adds the current search term to the search history.
/// </summary>
    that.recentSearches.unshift(that.searchTerm());
    saveState();
  }
}

A função de addSearchTermToRecentSearches usa a função de conveniência Knockout, unshift, que adiciona um item para o Iniciar da matriz. Sempre que uma pesquisa recente é adicionada, o estado é armazenado usando armazenamento local do HTML5. Neste caso o estado do array é convertido em uma lista separada por vírgulas via a função toString e convertido novamente via divisão. Um ViewModel mais complexo muito provavelmente iria salvar vários valores de propriedade em formato JSON.

Curiosamente, enquanto o navegador Windows Phone suporte a armazenamento local, esta função é desativada quando o navegador processa páginas do armazenamento isolado. Para que o código anterior funcione, a equipe de Cordova tinha que escrever uma implementação de "correção" de armazenamento local APIs que salva o estado dentro de armazenamento isolado do telefone.

Com esta última alteração para o aplicativo de busca do Twitter, agora é totalmente funcional.

Prova de portabilidade: Executando em um iPhone

Como você pode ver, o quadro de Cordova torna possível criar aplicativos baseados em HTML5 para Windows Phone. Também é possível imitar o Metro look and feel nativo usando técnicas simples de HTML5 e CSS, enquanto estruturas como Knockout permitem estruturar adequadamente seu código.

Este artigo tem focado na criação de aplicativos para Windows Phone, mas o aplicativo Twitter Search é portátil e deve ser executado em um iPhone ou Android telefone sem modificação. Mas um aplicativo de estilo de Metro se adequar esses telefones?

Como uma demonstração final a versatilidade desta abordagem, eu criei uma versão iOS do aplicativo Twitter Search, usando jQuery Mobile para imitar a aparência nativa. Isto faz grande uso do padrão MVVM, em que apenas o modo de exibição precisa ser alterado — toda a lógica de ViewModel é exatamente o mesmo. Usando o serviço de compilação baseados em nuvem foi capaz de criar um pacote de "ipa" iOS e instalá-lo em um iPhone, tudo a partir de uma máquina Windows. Você pode ver os dois aplicativos em execução lado a lado em Figura 16.

Twitter Search Running on an iPhone and a Windows Phone Device
Figura 16 Twitter pesquisa em execução em um dispositivo de Windows Phone e um iPhone

O código-fonte completo para o iOS e versões de Windows Phone do aplicativo acompanha este artigo.

Colin Eberhardt é um arquiteto técnico Scott Logic Ltd. e principal arquiteto no Visiblox (visiblox.com), que fornece controles de gráficos para uma gama de Microsoft.Tecnologias .NET Framework. Você pode segui-lo no Twitter em twitter.com/ColinEberhardt.

Graças aos seguintes especialistas técnicos para revisão deste artigo: Olivier Bloch, Glen Gordon e Jesse MacFadyen