Il presente articolo è stato tradotto automaticamente.

ASP.NET MVC 3

Sviluppo di applicazioni Web native e mobili ibride

Shane Chiesa

Scaricare il codice di esempio

Si vuole costruire un'applicazione mobile ma sei disorientato dalla matrice di periferiche disponibili e di API per imparare.Quale piattaforma mobile dovrebbe scegliere?L'iOS di Apple (iPhone e iPad) utilizza Objective C, Google Android utilizza Java e Windows Phone utilizza Silverlight, ma ognuna di queste opzioni ha un'API distinta e un mercato distinto.La scelta di concentrarsi su una pila di particolare tecnologia potrebbe lasciare il 50 per cento del mercato — o più — in grado di utilizzare l'applicazione.Se si sceglie di provare a sostenere tutte queste piattaforme, si hanno almeno tre distinte basi di codice per mantenere, aumentando significativamente i costi di sviluppo e manutenzione.

C'è un'altra opzione: Si potrebbe costruire un'applicazione Web mobile, perché può essere visualizzata su qualsiasi di questi dispositivi.Ma questo approccio ha anche alcuni ostacoli.Il più grande ostacolo per lo sviluppo di un'applicazione di intera azienda utilizzando HTML e JavaScript è la mancanza di accesso a molte caratteristiche hardware nativa dispositivo come la macchina fotografica, GPS o accelerometro.

Chiaramente il mercato della telefonia mobile è solo andare a crescere, così come fare sostenere tutte queste opzioni dispositivo mentre fornisce la migliore esperienza utente possibile?In questo articolo mostreremo come costruire un'applicazione mobile che sfrutta il meglio dei due mondi eseguendo il wrapping di un'applicazione Web mobile con un guscio di un'applicazione nativa.

Il concetto di applicazione ibrida

Il concetto alla base di un'applicazione ibrida è a capo di un'applicazione Web ottimizzate in un guscio di applicazione nativa specifiche del dispositivo.La shell di applicazione nativa ospita un controllo del browser Web configurato per lanciare l'applicazione mobile specifico URL quando lancia l'applicazione shell.Altri elementi dell'interfaccia utente possono essere forniti in shell applicazione nativa come stato necessario, ma solo il controllo del browser Web è obbligatorio.Il controllo di browser Web nativo poi Ascolta gli URL vengono richiesti come l'utente si sposta il sito.Quando l'utente richiede un URL specifico che richiede funzionalità nativa, il controllo del browser Web interrompe l'evento di navigazione e richiama invece le funzionalità native.Come l'utente completa il processo nativo, l'applicazione consente di passare il controllo del browser Web nuovamente dentro il sito Web di flusso nella posizione appropriata.

Per illustrare come questo è fatto, vado a piedi attraverso la mia esperienza di creazione di un'applicazione con i miei colleghi EffectiveUI per un cliente.Costruito per un lavoratore del campo mobile che elabora un certo numero di ordini di lavoro di manutenzione per i beni comunali, come segni, panchine e idranti antincendio, l'applicazione si avvale delle funzionalità browser supportato per ottenere la posizione corrente dell'utente e accesso all'hardware nativo di scattare foto di attivi e caricarle su server.Figura 1 mostra il menu principale dell'applicazione compilata.

The Completed Application Main MenuFigura 1 nel Menu principale applicazione completata

Creazione dell'applicazione Web

Durante la creazione di questa applicazione mobile, ho seguito una serie di suggerimenti da un articolo di Steve Sanderson, "Costruire un migliore Mobile Browsing Experience" (msdn.microsoft.com/magazine/hh288079) nel numero di luglio 2011 di MSDN Magazine.Oltre alle raccomandazioni in questo articolo, ho imparato alcune cose lungo la strada:

  • Ottimizzare gli elementi dell'interfaccia per toccare più utenti di telefonia mobile utilizza interazione basata su tocco.Tocco interazione è intrinsecamente meno preciso interazione basata su mouse sul desktop.Tutti gli elementi interattivi quali pulsanti e voci di menu devono essere proporzionalmente più grande nell'interfaccia mobile di quanto siano in esperienza desktop.
  • Ottimizzare Your Mobile visualizzazioni per larghezza di banda più dispositivi mobili sono risorsa vincolata, soprattutto se si considera la larghezza di banda.Non forzare l'utente a scaricare una serie di immagini di grandi dimensioni per poter utilizzare il sito.Gli utenti su dispositivi mobili si aspettano interfacce reattive e rapidamente abbandoneranno al sito o all'applicazione se esso non esegue alle loro aspettative.
  • Browser Web mobile uso HTML5 e CSS3 perché non hanno l'eredità lunga dei browser desktop, sono molto più veloce di adottare gli standard emergenti di HTML5 e CSS3 rispetto ai loro omologhi desktop.In molti casi, i browser mobili sono molto più avanti del browser desktop nell'attuazione di queste caratteristiche.Approfitta di questa nel vostro punto di vista mobile per alleggerire il carico utile che il browser mobile ha bisogno di scaricare e lasciare che il browser fare di più del rendering stilistico.

Uno dei requisiti tecnici dal mio cliente durante la creazione di questa applicazione è stato per dimostrare la logica di controllore condivisione tra punti di vista desktop e mobili del sito.Questo requisito è comune a molti clienti e dovrebbe essere favorito dagli sviluppatori come bene, come semplifica notevolmente il processo di creazione di un'applicazione che supporta gli utenti desktop e mobili.ASP.NET MVC 3 fornisce la possibilità di passare a vista basata su elementi di richiesta, come il browser richiedente, pur condividendo ancora controller e modelli tra più visualizzazioni.Inoltre permette allo sviluppatore di controllare finemente l'esperienza sul sito per ognuna delle piattaforme differenti, che significa lo sviluppatore sola esigenze per costruire la logica di business, una volta e poi adattare la presentazione per ogni piattaforma.Figura 2 mostra una funzione di utilità per decidere quale visualizzare a presentare.

Figura 2 Utility per decidere quale visualizzare al presente

private ActionResult SelectView(string viewName, object model,
  string outputType = "html")
{
  if (outputType.ToLower() == "json")
  {
    return Json(model, JsonRequestBehavior.AllowGet);
  }
  else
  {
    #if MOBILE
      return View(viewName + "Mobile", model);
    #else
      if (Request.Browser.IsMobileDevice)
      {
        return View(viewName + "Mobile", model);
      }
      else
      {
        return View(viewName, model);
      }
    #endif
  }
}

La funzione di utilità mi ha permesso di soddisfare l'esigenza di condividere lo stesso codice per prendere la decisione su quale view per presentare all'utente in base alla richiesta in arrivo. Se la richiesta in arrivo è uno script che richiede JSON invece di HTML, il controller può anche rispondere in modo appropriato utilizzando le stesse classi di logica e modello di affari semplicemente impostando il parametro outputType in modo appropriato. Io uso anche una dichiarazione precompilatore cercando il simbolo di compilazione condizionale MOBILE per attivare il debug il punto di vista mobile utilizzando il mio browser desktop. Questo è stato attivato mediante un ulteriore costruire come bersaglio, "Mobile", in ASP.NET MVC 3 del progetto e mi ha permesso di ignorare il controllo per Request.Browser.IsMobileDevice nella configurazione del debug desktop, migliorando notevolmente mia efficienza nella costruzione e la versione dell'applicazione mobile di debug.

Durante la creazione dell'applicazione separate pagine master ho anche usato per le versioni desktop e mobile del sito. Le versioni desktop e mobili della pagina master sono significativamente diverse per affrontare le disparità nella presentazione tra le piattaforme. La pagina master mobile include il mio file CSS specifici del mobile e una struttura di layout semplificato per facilitare lo sviluppo di singole viste utilizzando il markup Mobile Framework jQuery e sintassi.

Tutte le piattaforme mobili moderne permettono l'accesso a radio GPS di un dispositivo per determinare la posizione corrente dell'utente attraverso la geolocalizzazione HTML5 World Wide Web Consortium (W3C) API. L'uso della geolocalizzazione API è stata discussa in dettaglio nell'articolo di Brandon Satrom, "Integrazione di geolocalizzazione in Web Applications" (msdn.microsoft.com/magazine/hh580735) nel numero di dicembre 2011. Anche se tale articolo viene illustrato l'utilizzo di un polyfill HTML5 JavaScript per sostenere la posizione su browser che non supportano nativamente il HTML5 geolocation API, più attuali mobile browser supportano l'HTML5 geolocation API nativamente, quindi la tecnica polyfill, molto probabilmente non è necessaria. Si dovrebbe prendere in considerazione i dispositivi e i browser che destinazione mentre si sta valutando la necessità di utilizzare la tecnica di polyfill. Una cosa da notare in particolare per Android è che avrete bisogno per assicurarsi che il parametro enableHighAccuracy nella chiamata geolocalizzazione è impostata su "true" al fine di accedere con successo la funzionalità GPS nell'emulatore di Android, come mostrato nella Figura 3.

Figura 3 Geolocation usando HTML5

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(function(position) {
    $("#map_canvas").GoogleMap("addMarker", {
      id: "device_location",
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
      description: "Current Location",
      iconImageUrl: '@Url.Content("~/Content/images/my-location-dot.png")',
      callback: function() {
        $("#map_canvas").GoogleMap("panToLocation", {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude
        });
      }
    });
  }, function(error) {}, {
    enableHighAccuracy: true
  });
}

Utilizzando jQuery Mobile

Mobile Framework jQuery è "un sistema di interfaccia utente basata su HTML5 unificato per tutte le piattaforme popolare dispositivo mobile," secondo sito Web del progetto (jquerymobile.com). Contiene un numero di widget touch-ottimizzata e facilita enormemente il compito di costruire applicazioni Web mobili che aspetto come native di applicazioni mobili. jQuery Mobile può essere aggiunti all'applicazione ASP.NET MVC 3 del progetto attraverso NuGet utilizzando l'interfaccia NuGet Package Manager o dalla Console di gestione pacchetti eseguendo il comando "pacchetto di installazione jquery.mobile." Aggiunge i jQuery Mobile JavaScript e CSS file al progetto. Sarà ancora necessario aggiungere riferimenti ai file jQuery Mobile JavaScript e CSS per la pagina master mobile, come mostrato nella Figura 4.

Figura 4 Mobile Master Page

    <!DOCTYPE html>
    <html>
    <head>
      <title>@ViewBag.Title</title>
      <meta name="viewport" content="width=device-width,
        initial-scale=1.0, user-scalable=no, height=device-height" />
      <meta http-equiv="Content-type" content="text/html; charset=utf-8">
      <link href="@Url.Content("~/Content/eui_assets/css/reset.css")"
        rel="stylesheet" type="text/css" />
      <link href="@Url.Content("~/Content/jquery.mobile-1.0.min.css")"
        rel="stylesheet" type="text/css" />
      <link href="@Url.Content("~/Content/mobile.css")"
        rel="stylesheet" type="text/css" />
      <script src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")"
        type="text/javascript"></script>
      @RenderSection("PreJQueryMobileInit", false)
      <script src="@Url.Content("~/Scripts/jquery.mobile-1.0.min.js")" 
        type="text/javascript"></script>
      <script type="text/javascript">
        $('a[data-ajax="false"]').live('click', function (event) {
          if (!$(this).hasClass("camera-link")) {
            $.mobile.showPageLoadingMsg();
          }
        });
      </script>
      @RenderSection("Head", false)
    </head>
    <body class="eui_body" id="@ViewBag.BodyID">
      @RenderBody()
    </body>
    </html>

jQuery Mobile apportare alcune modifiche significative per i modelli con cui qualsiasi jQuery sviluppatore è familiare. Per citare la documentazione Mobile di jQuery:

La prima cosa che impara in jQuery è chiamare codice all'interno della funzione $(document).ready() in modo che tutto ciò che verrà eseguito non appena viene caricato il DOM. Tuttavia, in jQuery Mobile, [AJAX] viene utilizzata per caricare il contenuto di ogni pagina nel DOM come naviga, e il gestore pronto DOM viene eseguita soltanto per la prima pagina. Per eseguire il codice ogni volta che una nuova pagina è caricata e creata, è possibile associare all'evento pageinit.

Ho usato l'evento pageinit all'interno di tutte le pagine dell'applicazione che conteneva il controllo di Google Maps per inizializzare la mappa quando la pagina viene modificata in vista tramite AJAX.

Una funzione aggiuntiva della pagina master mobile è la linea di @ RenderSection("PreJQueryMobileInit", false), mostrata in Figura 4, che consente di eseguire script prima di jQuery Mobile viene inizializzata sulla pagina. Nell'applicazione di esempio ho usato questa caratteristica per associare all'evento mobileinit così potuto impostare un callback personalizzato quando il comportamento di filtro Mobile listview jQuery è completo. Ho anche aggiunto due righe di codice alla libreria jQuery Mobile per aggiungere un metodo filterCompleteCallback al prototipo listview al fine di ottenere una notifica quando il filtro incorporato elenco era completo. Questo mi ha permesso di aggiornare le voci corrispondenti sulla mappa per abbinare elenco filtrato. La funzione di callback necessarie per essere aggiunto al controllo listview Mobile jQuery prima jQuery Mobile è stato applicato a qualsiasi del markup; che il codice viene eseguito nel gestore eventi mobileinit mostrato in Figura 5.

Figura 5 vincolante per l'evento mobileinit

if(navigator.geolocation) {   
  navigator.geolocation.getCurrentPosition(function (position) {
    $("#map_canvas").GoogleMap("addMarker", {
      id: "device_location",
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
      description: "Current Location",
      iconImageUrl: '@Url.Content("~/Content/images/my-location-dot.png")',
      callback: function () {
        $("#map_canvas").GoogleMap("panToLocation", {
          latitude: position.coords.latitude,
          longitude: position.coords.longitude
        });
      }
    });
  }, function (error) {
  }, {
    enableHighAccuracy: true
  });
}
@section PreJQueryMobileInit {
  <script type="text/javascript">
    $(document).bind("mobileinit", function () {
      $.mobile.listview.prototype.options.filterCompleteCallback = function () {
        // Note that filtercompletecallback is a custom
        // addition to jQuery Mobile and would need to be updated
        // in future revisions.
// See comments in jquery.mobile-1.0.js with SSC 09/12/2011
        var ids = [];
        var $visibleItems = $("#js-work-orders-list").find(
          "li:not(.ui-screen-hidden");
        for (var i = 0; i < $visibleItems.length; i++) {
          var item = $($visibleItems[i]).find("p");
          ids.push(item.text().substr(item.text().indexOf('#') + 1));
        }
        ids.push("device_location");
        $("#map_canvas").GoogleMap("hideAllMarkersExceptList", ids);
      }
    });
  </script>
}

jQuery Mobile sfrutta significativo delle nuove caratteristiche di HTML5, come i tag di intestazione e piè di pagina e dati-* attributi. Gli attributi di ruolo dati per determinare il comportamento che dovrebbe essere fissato su un elemento specificato. Ad esempio, nella visualizzazione MapMobile.cshtml in Figura 6, io ho due div definito con il ruolo di data = "pagina" attributo.

Figura 6 MapMobile.cshtml Markup

    <div data-role="page" id="map_page" data-fullscreen="true"
      data-url="map_page" data-theme="a">
      <header data-role="header" data-position="fixed">
        <a href="@Url.Action("Index", "Home")" data-icon="home"
          data-direction="reverse">Home</a>
        <h1>Map Demo</h1>
        <a href="#" data-icon="back" id="js-exit-street-view"
          class="ui-btn-hidden">Exit Street View</a>
      </header>
      <div data-role="content" class="main-content">
        <div id="map_canvas" style="width:100%;height:100%"></div>
      </div>
      <footer data-role="footer" data-position="fixed"
        data-id="fixed-nav" data-theme="a">
        <nav data-role="navbar">
          <ul>
            <li><a href="#map_page" class="ui-btn-active
              ui-state-persist">Map</a></li>
            <li><a href="#items_page">Work Orders</a></li>
          </ul>
        </nav>
      </footer>
    </div>
    <div data-role="page" id="items_page" data-url="items_page" data-theme="a">
      <header data-role="header" data-position="fixed">
        <a href="@Url.Action("Index", "Home")" data-icon="home"
          data-direction="reverse">Home</a>
        <h1>Map Demo</h1>
      </header>
      <div data-role="content" class="main-content">
        <div class="list-container">
          <ul data-role="listview" id="js-work-orders-list" data-filter="true">
          @foreach (MapItem item in Model.Items)
      {
          <li class="work-order-id-@item.ID">
            <a href="@Url.Action("Details", "Home", new { id = item.ID })"
              data-ajax="false">
              <h3>@item.Issue</h3>
              <p>Work Order #@item.ID</p>
            </a>
          </li>
        }
          </ul>
        </div>
      </div>
      <footer data-role="footer" data-position="fixed"
        data-id="fixed-nav" data-theme="a">
        <nav data-role="navbar">
          <ul>
            <li><a href="#map_page" data-direction="reverse">Map</a></li>
            <li><a href="#items_page" class="ui-btn-active
              ui-state-persist">Work Orders</a></li>
          </ul>
        </nav>
      </footer>
    </div>

Questo attributo racconta jQuery Mobile che ognuno di questi div deve essere trattato come una pagina separata sul dispositivo mobile e alla transizione tra di loro utilizzando AJAX senza una pagina di navigazione che si verificano nel browser. Questo produce l'effetto indicato negli screenshots in Figura 7. Il sito Web Mobile jQuery fornisce raccomandazioni e maggiori dettagli su come utilizzare ciascuna di dati-* attributi nel contesto Mobile jQuery.

Transitioning Between Pages Via AJAX
Figura 7 transizione tra pagine tramite AJAX

Costruire i gusci di applicazione nativa Mobile

Lo schema di base nello sviluppo di ognuna delle conchiglie applicazione nativa è progettare un'applicazione che contiene semplicemente un controllo del browser Web schermo intero. All'interno di questo controllo mi cattura l'evento che viene generato quando l'utente richiede una nuova pagina e confrontare l'URL richiesto contro un elenco di URL noti che deve richiamare funzionalità native. Questo è dove accade la "magia" di un'applicazione Web-based in un guscio di un'applicazione nativa. Ai fini di questa applicazione, l'URL corrispondente all'interno del sito è "Home/immagine" per richiamare la funzionalità nativa della fotocamera. Quando l'utente è nella pagina Dettagli ordine di lavoro, vedrete un icona nell'angolo superiore destro dello schermo della fotocamera come illustrato nella Figura 8. Cliccando su questa icona richiama la telecamera nativa.

Invoking Native Camera Functionality
Figura 8 richiamando le funzionalità Native di fotocamera

Windows Phone

Windows Phone utilizza Silverlight per tutte le funzionalità native. In qualche modo, in questo modo Windows Phone la piattaforma più semplice per il supporto per lo sviluppatore Web mobile che abbia familiarità con ASP.NET. Il layout di base XAML per la shell di applicazione nativa è semplice, come illustrato di seguito:

<Canvas x:Name="LayoutRoot" Background="Black" Margin="0">
  <phone:WebBrowser HorizontalAlignment="Left" Name="webBrowser1" 
    Navigating="webBrowser1_Navigating" IsScriptEnabled="True"
    IsGeolocationEnabled="True"
    Background="Black" Height="720" Width="480" />
</Canvas>

Gli elementi chiavi da notare sono qui che IsScriptEnabled è impostato su true — perché, per impostazione predefinita, il controllo del browser Web in Windows Phone non Abilita script — e che io sto gestendo l'evento Navigating.

Nel MainPage.xaml.cs, dimostrato in Figura 9, io gestire l'evento webBrowser1_Navigating. Se l'URL di navigazione corrisponde l'URL che sto cercando, io scegliere l'ID dell'ordine di lavoro che sto lavorando con e richiamare i nativi CameraCaptureTask mentre annullando la navigazione del browser Web. Dopo che l'utente richiede la foto con la fotocamera, il photoCaptureOr­viene richiamato il metodo SelectionCompleted. Qui, carica l'immagine al server Web utilizzando lo stesso modulo HTTP azione POST che sarebbe utilizzando il sito Web se ho presentato un form contenente un pulsante ingresso upload di file. Quando viene completato il caricamento di foto, upload_FormUploadCompleted viene richiamato, tornando all'utente per il flusso di applicazione Web.

Figura 9 Windows Phone MainPage.xaml.cs

public partial class MainPage : PhoneApplicationPage
{
  CameraCaptureTask cameraCaptureTask;
  BitmapImage bmp;
  string id = "";
  string baseURL = "http://...";
  // Constructor
  public MainPage()
  {
    InitializeComponent();
    cameraCaptureTask = new CameraCaptureTask();
    cameraCaptureTask.Completed +=
      new EventHandler<PhotoResult>(photoCaptureOrSelectionCompleted);
  }
  private void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
  {
    // Catch Navigation and launch local camera
    if (e.Uri.AbsoluteUri.ToLower().Contains("home/image"))
    {
      id = e.Uri.AbsoluteUri.Substring(e.Uri.AbsoluteUri.LastIndexOf("/") + 1);
      cameraCaptureTask.Show();
      e.Cancel = true;
    }
  }
  void photoCaptureOrSelectionCompleted(object sender, PhotoResult e)
  {
    if (e.TaskResult == TaskResult.OK)
    {
      byte[] data = new byte[e.ChosenPhoto.Length];
      e.ChosenPhoto.Read(data, 0, data.Length);
      e.ChosenPhoto.Close();
      Guid fileId = Guid.NewGuid();
      Dictionary<string, object> postParameters = new Dictionary<string, object>();
      postParameters.Add("photo", new FormUpload.FileParameter(
        data, fileId.ToString() +
        ".jpg", "image/jpeg"));
      FormUpload upload =
        new FormUpload(baseURL + "Home/UploadPicture/" + id, postParameters);
      upload.FormUploadCompleted +=
        new FormUpload.FormUploadCompletedHandler(upload_FormUploadCompleted);
      upload.BeginMultipartFormDataPost();
    }
  }
  void upload_FormUploadCompleted(object source)
  {
    webBrowser1.Navigate(webBrowser1.Source);
  }
  private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
  {
    webBrowser1.Navigate(new Uri(baseURL));
  }
}

Windows Phone ha alcuni comportamenti diversi quando si interagisce con la versione Web-based di Google Maps o mappe Bing controlli se confrontato con Android o iOS. A causa del modo il 9 di Internet Explorer browser mobile acquisisce il tocco, sfiorare e pizzico gesti senza passare attraverso il motore JavaScript, le mappe basate sul Web non possono ingrandire o padella con gesti e deve utilizzare lo zoom o padella controlli forniti dalla mappa. Data questa limitazione, un futuro miglioramento a questo progetto sarebbe di invocare il controllo nativo Bing Mappe su Windows Phone dove è richiesta la funzionalità mappa interattiva e poi tornare all'applicazione Web su schermi che non richiedono la funzionalità di mappa interattiva.

Androide

Il codice Java per Android è stato scritto dal mio collega, Sean Christmann ed è simile al codice per Windows Phone. La disposizione seguente XML definisce un layout a schermo intero per il controllo di WebView Android:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <WebView android:id="@+id/webView"
      android:layout_width="match_parent" 
      android:layout_height="match_parent"></WebView>
</LinearLayout>

All'interno di EffectiveUIActivity.java, mostrato in Figura 10, l'override per onCreate imposta il WebViewClient per eseguire l'override del onLoadResource e shouldOverrideUrlLoading metodi del controllo WebView per cercare la stessa stringa corrispondente, come nel Windows Phone e, se trovato, crea l'attività della fotocamera e Annulla la navigazione. Il codice esegue l'override onGeolocationPermissionsShowPrompt per sopprimere il prompt dei comandi per l'utente che si verifica ogni volta che l'applicazione venga eseguita per consentire l'autorizzazione per il controllo di visualizzazione Web accedere la posizione GPS. Dopo che viene eseguita l'attività della fotocamera, la funzione di onActivityResult posti l'immagine al server Web utilizzando lo stesso metodo, come il precedente esempio di Windows Phone e quindi restituisce all'utente per il flusso di applicazione Web.

Figura 10 androide EffectiveUIActivity.java

    public class EffectiveUIActivity extends Activity {
      /** Called when the activity is first created.
    */
      WebView webView;
      String cameraId;
      static String baseURL = "http://...";
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        webView = (WebView)findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setGeolocationEnabled(true);
        webView.setVerticalScrollbarOverlay(true);
        webView.loadUrl(baseURL);
        final EffectiveUIActivity activity = this;
        webView.setWebViewClient(new WebViewClient(){
          @Override
          public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
            if(url.contains("Home/Image")){
              activity.createCamera();
            }
          }
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url){
            String match = "Home/Image/";
            int i = url.indexOf(match);
            if(i>0){
              cameraId = url.substring(i+match.length());
              activity.createCamera();
              return true;
            }
            return false;
          }
        });
        webView.setWebChromeClient(new WebChromeClient(){
          @Override
          public void onGeolocationPermissionsShowPrompt(
            String origin, GeolocationPermissions.Callback callback) {
            super.onGeolocationPermissionsShowPrompt(origin, callback);
            callback.invoke(origin, true, false);
          }
        });       
      }
      public void createCamera(){
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        startActivityForResult(intent, 2000);
      }
      @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
          if (resultCode == Activity.RESULT_OK && requestCode == 2000) {
            Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            thumbnail.compress(CompressFormat.JPEG, 75, bos);
            byte[] imagebytes = bos.toByteArray();
            ByteArrayBody bab = new ByteArrayBody(imagebytes, "image/jpeg",
              UUID.
    nameUUIDFromBytes(imagebytes).toString()+".jpg");
            HttpClient client = new DefaultHttpClient();
            HttpPost post = new HttpPost(baseURL+"Home/UploadPicture");
            MultipartEntity reqEntity =
              new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
            reqEntity.addPart("photo", bab);
            try {
              reqEntity.addPart("ID", new StringBody(cameraId, "text/plain",
                Charset.forName( "UTF-8" )));
            } catch (UnsupportedEncodingException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
              }
              post.setEntity(reqEntity);
              try {
                HttpResponse response = client.execute(post);
                BufferedReader reader = new BufferedReader(
                  new InputStreamReader(
                  response.getEntity().getContent(), "UTF-8"));
                String sResponse;
                StringBuilder s = new StringBuilder();
                while ((sResponse = reader.readLine()) != null) {
                  s = s.append(sResponse);
                }
              } catch (ClientProtocolException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                }
                webView.loadUrl(webView.getUrl());
              }
            }
    }

iOS

Il codice Objective-C per iOS è stato anche scritto dal mio collega, Sean Christmann, ed è anche simile a quello usato per Windows Phone e Android. All'interno WebCameraViewController.m mostrato in Figura 11, il controllo UIWebView esegue il metodo shouldStartLoadWithRequest per fare il pattern matching su URL richiesto. Se la stringa dell'URL corrisponde, il codice restituisce "NO" per annullare lo spostamento e richiama il UIImagePickerController nativo. Questo permette all'utente di scegliere un'immagine da Fototeca o prendere una nuova immagine con la fotocamera a bordo. Dopo aver selezionato l'immagine, il codice articoli poi l'immagine al server Web utilizzando la libreria ASIFormDataRequest (allseeing-i.com/ASIHTTPRequest) prima di restituire il UIWebView torna al flusso normale applicazione.

Figura 11 iOS codice

- (void) choosefromCamera {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.mediaTypes = [NSArray arrayWithObjects:(NSString*)kUTTypeImage, nil];
    if ([UIImagePickerController
      isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
      picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    }else{
      picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    }
    [self presentModalViewController:picker animated:YES];
}
- (void)imagePickerController:(UIImagePickerController *)picker   
    didFinishPickingMediaWithInfo:(NSDictionary *)info {
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSData *jpg = UIImageJPEGRepresentation(image, 0.3);
    [picker dismissModalViewControllerAnimated:YES];
    [picker release];
    NSString *url =
      [NSString stringWithFormat:@"%@:7511/WorkOrders/UploadPicture", baseURL];
    ASIFormDataRequest *request =
      [ASIFormDataRequest requestWithURL:[NSURL URLWithString:url]];
    [request addData:jpg withFileName:[
      NSString stringWithFormat:@"%@.jpg", [self GetUUID]]
      andContentType:@"image/jpeg" forKey:@"photo"];
    [request addPostValue:cameraId forKey:@"ID"];
    [request setDelegate:self];
    [request setDidFinishSelector:@selector(imageUploaded:)];
    [request setDidFailSelector:@selector(imageUploaded:)];
    [request startSynchronous];
    [webView reload];
}
-(void) imageUploaded:(ASIFormDataRequest *)request {
    NSString *response = [request responseString];
    NSLog(@"%@",response);
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(
  NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];
    NSString *str = [url absoluteString];
    NSRange range = [str rangeOfString:@"WorkOrders/Image/"];
    if (range.location != NSNotFound) {
      cameraId = [str substringFromIndex:range.location+17];
      [cameraId retain];
      NSLog(@"%@", cameraId);
      [self choosefromCamera];       return NO;
    }else{
      return YES;
    }
}

Grazioso degradazione dell'esperienza Mobile

Che cosa succede se l'utente del sito Web mobile non è usare la shell applicazione nativa per accedere la fotocamera? In questo scenario, è importante avere un grazioso degrado dell'esperienza utente. Degradazione aggraziato è il concetto di costruire l'applicazione affinché essa continua a funzionare correttamente anche se esso è visto con il software di ottimali. Questo non significa che ogni caratteristica funziona esattamente allo stesso modo o che addirittura sembra simile all'esperienza previsto, ma essa mira a garantire che tutti i fondamentali obiettivi dell'utente può ancora essere compiuti anche se l'utente non è sempre la migliore esperienza.

Per attivare il grazioso degrado in questa applicazione, ho costruito una pagina ASP.NET MVC 3 controller e vista per l'URL di acquisizione di immagini, "Home/immagine," che è essere catturato dai gusci di applicazione nativa per fornire un semplice file caricare il modulo come mostrato nella Figura 12. Questo modulo consente agli utenti che non utilizzano i gusci mobili avanzati per eseguire lo stesso compito di aggiungere un'immagine a un ordine di lavoro, anche se essi non sono sempre l'esperienza integrata. Il modulo impieghi la stessa azione di controllore utilizzata dai gusci di applicazione nativa, incoraggiando il riutilizzo del codice tra tutte le diverse piattaforme e visualizzazioni.

A Simple File Upload Form for Graceful Degradation
Figura 12 semplici File Upload Form per la degradazione della graziosa

Significativi vantaggi di costo

L'approccio di applicazione ibrida può fornire vantaggi significativi costi uniche applicazioni native, sia nel breve e lungo termine. Strumenti come jQuery Mobile restringono le differenze di usabilità, che possono portare a vantaggi significativi affari dove non è richiesto l'accesso nativo del dispositivo.

Con i dispositivi mobili proliferando a macchia d'olio, si hanno alcune scelte quando cercando di costruire un'applicazione mobile:

  • Costruire un'applicazione nativa per ogni piattaforma si desidera sostenere questo ha il vantaggio di fornire la migliore esperienza utente e le prestazioni per ogni piattaforma pur consentendo l'accesso a tutte le funzionalità native del dispositivo e il potere di commercializzazione dei negozi app. Lo svantaggio, tuttavia, è che potrebbe essere notevolmente più costoso costruire e mantenere perché richiederà la base di codice per ogni piattaforma per sostenere un separato. Inoltre, ogni nuova versione dell'applicazione richiede che l'applicazione essere ripresentato ai depositi app.
  • Costruire un'applicazione Mobile Web questo ha il vantaggio di essere l'opzione più semplice ed economico per sviluppare, lanciare e aggiornamento per tutte le piattaforme, ma l'esperienza dell'utente può essere compromessa dalla mancanza di accesso alle funzionalità hardware nativo. Mancanza di accesso ai depositi app può anche compromettere adozione dell'applicazione, spingendo la commercializzazione dell'applicazione a voi tutti.
  • Costruire un ibrido nativo e Mobile Web Application Questo è l'approccio che ho discusso, che fornisce un solido compromesso tra gli alti costi di sviluppo di un'applicazione unica nativa per ogni piattaforma e la mancanza di accesso all'hardware nativa per un'applicazione Web mobile. Questa opzione fornisce anche accesso ai depositi app, aumentare la portata dell'applicazione.

È importante notare che nessuno di questi approcci è intrinsecamente migliore rispetto agli altri — tutti hanno i propri punti di forza e di debolezza. Un'analisi costi/benefici completi di ciascuna delle opzioni vi aiuterà a determinare quale percorso è quello giusto per gli utenti e il vostro business. Quando si effettua questa decisione, è importante prendere in considerazione l'esperienza dell'utente, i costi di sviluppo up-front e i costi di manutenzione in corso, nonché ulteriori fattori sottile come adozione marketing e utente.

Per molti scenari di applicazioni aziendali, sono favorevole per l'inclusione di un' Web mobile o ibrida, come ulteriore sforzo di costruire applicazioni native univoco per ogni piattaforma mobile potrebbe superare i vantaggi aziendali. Scenari di business necessario rivedere con attenzione entro i confini di una distribuzione di applicazioni Web o ibrida mobile.

Applicazioni mobili sono qui per rimanere e sono sempre più importanti come l'informatica turni lontano dalla tradizionale esperienza desktop in una matrice di esperienze mobile. Come si guarda per costruire applicazioni nello spazio mobile, ricordare che compromesso non è sempre una parolaccia e che esso può comportare il meglio dei due mondi.

Shane Chiesa è un vantaggio tecnico per EffectiveUI in Denver, Colorado Egli ha sviluppato in Microsoft.NET Framework con un focus su ASP.NET e Microsoft mobile technologies dal 2002. Il suo blog è situata a s church.net. Saperne di più su EffectiveUI a effectiveui.com.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: **Dr.**James McCaffrey