Este artículo proviene de un motor de traducción automática.

ASP.NET MVC 3

Desarrollo de aplicaciones web móviles e híbridas

Shane Church

Descargar el código de ejemplo

Desea construir una aplicación móvil pero está desconcertado por la variedad de dispositivos disponibles y APIs para aprender.¿Qué plataforma móvil debería elegir?El iOS de Apple (iPhone y iPad) utiliza Objective C, Google Android utiliza Java y Windows Phone utiliza Silverlight, pero cada una de estas opciones tiene una API distinta y un mercado definido.Eligiendo centrarse en una pila de tecnología particular podría dejar un 50 por ciento del mercado — o más — no se puede utilizar la aplicación.Si elige intentar apoyar todas estas plataformas, tiene al menos tres distintos códigos base para mantener, aumentar considerablemente los costes de desarrollo y mantenimiento.

No hay otra opción: Podría crear una aplicación Web móvil, porque puede verse en ninguno de estos dispositivos.Pero este enfoque también tiene algunos obstáculos.El mayor obstáculo para el desarrollo de una aplicación de todo negocio usando HTML y JavaScript es la falta de acceso a muchas funciones de hardware de dispositivos nativos como la cámara, el GPS o el acelerómetro.

Claramente el mercado móvil sólo va a crecer, entonces, ¿cómo apoyamos todas estas opciones de dispositivo mientras proporciona la mejor experiencia de usuario posible?En este artículo te mostraré cómo crear una aplicación móvil que aprovecha lo mejor de ambos mundos por una aplicación Web móvil con una concha de una aplicación nativa de ajuste.

El concepto de aplicación híbrida

El concepto básico de una aplicación híbrida es envolver una aplicación Web móvil optimizada en una cáscara de aplicación nativa específica del dispositivo.El shell de aplicación nativa aloja un control de navegador de Web está configurado para iniciar la URL de la aplicación móvil específica cuando se inicia la aplicación de shell.Otros elementos de la IU pueden suministrarse en el shell de aplicación nativa según sea necesario, pero sólo el control del explorador Web es necesario.El control del explorador Web nativo, a continuación, escucha a las direcciones URL que se solicita que el usuario navega por el sitio.Cuando el usuario solicita una dirección URL específica que requiere funcionalidad nativa, el control del explorador Web interrumpe el evento de navegación y en su lugar invoca la funcionalidad nativa.Como el usuario completa el proceso nativo, la aplicación desplaza el control del explorador Web en el sitio Web de flujo en la ubicación adecuada.

Para ilustrar cómo se hace, voy camino a través de mi experiencia en la creación de una aplicación con mis colegas de EffectiveUI para un cliente.Construido para un trabajador de campo móvil que procesa una serie de órdenes de trabajo de mantenimiento de activos municipales, como signos, bancos y bocas de incendios, la aplicación aprovecha características navegador compatible para obtener la ubicación actual del usuario y acceso al hardware nativo para tomar fotografías de los activos y cargar en el servidor.Figura 1 muestra el menú principal de la aplicación completa.

The Completed Application Main MenuFigura 1 menú principal de la aplicación completada

Crear la aplicación Web

Al crear esta aplicación móvil, he seguido una serie de sugerencias del artículo de Steve Sanderson, "Construir un mejor móvil navegando experiencia" (msdn.microsoft.com/magazine/hh288079) en la edición de julio de 2011 de MSDN Magazine.Además de las recomendaciones de este artículo, he aprendido algunas cosas en el camino:

  • Optimizar los elementos de la IU para tocar más usuarios móviles utilizan interacción basada en el toque.La interacción táctil es intrínsecamente menos precisa que la interacción basada en el ratón en el escritorio.Todos los elementos interactivos como botones y elementos de menú que deba ser proporcionalmente mayor en la interfaz de móvil que están en la experiencia de escritorio.
  • Optimizar tu móvil vistas para ancho de banda más dispositivos móviles son recursos limitados, especialmente cuando se considera el ancho de banda.No forzar su usuario para descargar una serie de imágenes de gran tamaño para poder utilizar el sitio.Los usuarios de dispositivos móviles esperan interfaces sensibles y rápidamente abandonarán su sitio o aplicación si no realiza sus expectativas.
  • Exploradores de Web móviles de uso HTML5 y CSS3 porque no tienen la larga herencia de navegadores de escritorio, son mucho más rápidos adoptar los nuevos estándares HTML5 y CSS3 que sus contrapartes de escritorio.En muchos casos, los exploradores móviles están muy por delante de los navegadores de escritorio en la aplicación de estas características.Aprovechar esto en sus vistas móviles para aligerar la carga que el explorador móvil necesita descargar y dejar que el navegador a hacer más de la representación estilística.

Uno de los requisitos técnicos de mi cliente al crear esta aplicación era demostrar la lógica de controlador compartido entre móviles y de escritorio de vistas del sitio.Este requisito es común a muchos clientes y debe ser favorecido por los desarrolladores, así, como mucho optimiza el proceso de creación de una aplicación que admite usuarios móviles y de escritorio.ASP.NET MVC 3 proporciona la capacidad para cambiar vistas basadas en elementos de la solicitud, como el navegador, mientras sigue compartiendo modelos entre varias vistas y controladores.También permite al desarrollador controlar finamente la experiencia en el sitio para cada una de las plataformas diferentes, lo que significa que el programador sólo necesita crear la lógica de negocio de una vez y, a continuación, personalizar la presentación para cada plataforma.Figura 2 muestra una función de utilidad para decidir la vista que debe para presentar.

Utilidad de la figura 2 para decidir que ver 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 función de utilidad me permitió cumplir con el requisito de compartir el mismo código para tomar la decisión sobre qué vista para presentar al usuario basado en la solicitud entrante. Si la solicitud entrante es un script que solicita JSON en lugar de HTML, el controlador puede también responder apropiadamente mediante las mismas clases de lógica y modelo de negocios simplemente el valor del parámetro outputType adecuadamente. También utilizar una declaración precompilador buscando el símbolo de compilación condicional móvil para habilitar la depuración de las vistas móviles utilizando Mis navegadores de escritorio. Esto fue activado mediante una generación adicional de destino, "Mobile", en la aplicación ASP.NET MVC 3 proyecto y me permitieron saltarse la comprobación de Request.Browser.IsMobileDevice en la configuración de depuración escritorio, mejorando mucho mi eficiencia en la construcción y depuración de la versión móvil de la aplicación.

Mientras se construía la aplicación también utilicé páginas maestras separadas para las versiones móviles y de escritorio del sitio. Las versiones de escritorio y portátiles de la página maestra son significativamente diferentes para abordar las disparidades en la presentación entre las plataformas. La página principal móvil incluye archivos CSS móvil específica y una estructura de diseño simplificado para facilitar el desarrollo de vistas individuales mediante el marcado de jQuery Mobile Framework y sintaxis.

Todas las plataformas móviles modernas permiten el acceso a la radio de un dispositivo GPS para determinar la ubicación del usuario actual a través de la API de geolocalización de HTML5 World Wide Web Consortium (W3C). El uso de la API de geolocalización se examinó en detalle en el artículo de Brandon Satrom, "Integración de geolocalización en aplicaciones Web" (msdn.microsoft.com/magazine/hh580735) en la edición de diciembre de 2011. A pesar de que el artículo describe el uso de un polyfill HTML5 JavaScript para apoyar la ubicación en los navegadores que no admiten de forma nativa la geolocalización HTML5 API, exploradores móviles más actuales apoyan el HTML5 geolocalización APIs nativamente, lo más probable es que la técnica de polyfill no es necesaria. Debe tener en cuenta los dispositivos y exploradores que destino mientras está evaluando la necesidad de utilizar la técnica de polyfill. Una cosa es señalar específicamente para Android es que necesitará para asegurarse de que el parámetro enableHighAccuracy en la llamada de geolocalización se establece en "true" en orden al acceso correctamente la funcionalidad GPS en el emulador de Android, como se muestra en figura 3.

Figura 3 geolocalización utilizando 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
});
}

Utilizando jQuery Mobile

El jQuery Mobile Framework es "un sistema de interfaz de usuario unificada basada en HTML5 para todas las plataformas de dispositivo móvil popular," de acuerdo al sitio Web del proyecto (jquerymobile.com). Contiene una serie de widgets optimizados de toque y facilita enormemente la tarea de construir aplicaciones Web móviles ese aspecto como aplicaciones móviles nativas. jQuery Mobile puede añadirse a las aplicaciones Web ASP.NET MVC 3 proyecto a través de NuGet mediante la interfaz del administrador de paquetes de NuGet o desde la consola de administrador de paquetes ejecutando el comando "paquete de instalación jquery.mobile". Esto añade los archivos jQuery Mobile JavaScript y CSS a su proyecto. Aún necesitará agregar referencias a los archivos de jQuery Mobile JavaScript y CSS a tu página principal móvil, como se muestra en figura 4.

Figura 4 la página principal de Mobile

<!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 hacer algunas modificaciones significativas a los patrones con que cualquier jQuery desarrollador es familiar. Al citar el jQuery documentación móvil:

Lo primero que se aprende en jQuery es llamar a código dentro de la función $(document).ready() para que todo se ejecutará en cuanto se carga el DOM. Sin embargo, en jQuery Mobile, [AJAX] se utiliza para cargar el contenido de cada página en el DOM como navegar y el controlador listo DOM sólo ejecuta la primera página. Para ejecutar el código cuando se carga y se crea una nueva página, puede enlazar con el evento pageinit.

He utilizado el evento pageinit dentro de todas las páginas de la aplicación que contiene el control de Google Maps para inicializar el mapa cuando la transición de la página en la vista a través de AJAX.

Una característica adicional de la página principal móvil es la línea de @ RenderSection("PreJQueryMobileInit", false), se muestra en la figura 4, que le permite ejecutar secuencias de comandos antes de jQuery Mobile se ha inicializado en la página. En la aplicación de ejemplo he usado esta característica para enlazar con el evento mobileinit, por lo que podría configurar una devolución de llamada personalizado cuando el comportamiento del filtro de listview móvil jQuery es completa. También he añadido dos líneas de código a la biblioteca móvil de jQuery para agregar un método de filterCompleteCallback con el prototipo de listview para recibir una notificación cuando la lista integrada de filtrado fue completado. Esto me permitió actualizar los elementos coincidentes en el mapa para que coincida con la lista filtrada. La función de devolución de llamada debe agregarse a listview móvil jQuery jQuery Mobile fue aplicado a cualquiera de la marca; que el código se ejecuta en el controlador de eventos de mobileinit en figura 5.

Figura 5 vinculante para el 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 aprovecha significativo de nuevas características en HTML5, como las etiquetas de encabezado y pie de página y los datos-* atributos. Los atributos de la función de datos determinan el comportamiento que debe asociarse a un elemento determinado. Por ejemplo, en la vista de MapMobile.cshtml en figura 6, tengo dos divs definidos con la función de datos = atributo "page".

Figura 6 MapMobile.cshtml marca

<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>

Este atributo indica jQuery Mobile que cada uno de estos divs debe tratarse como una página separada en el dispositivo móvil y a la transición entre ellos utilizando AJAX sin una navegación de página en el navegador. Esto produce el efecto se muestra en las imágenes en figura 7. El sitio Web Mobile jQuery proporciona recomendaciones y más detalles sobre cómo utilizar cada uno de los datos-* atributos en el contexto de jQuery Mobile.

Transitioning Between Pages Via AJAX
Figura 7 transición entre páginas mediante AJAX

Construcción de las conchas de aplicaciones móviles nativas

El patrón básico en el desarrollo de cada uno de los proyectiles de aplicación nativa es diseñar una aplicación que simplemente contiene un control de explorador Web de pantalla completa. Dentro de este control captura el evento que se desencadena cuando el usuario solicita una página nueva y comparar la URL solicitada contra una lista de URL conocidas que debe invocar la funcionalidad nativa. Esto es donde pasa la "magia" de una aplicación basada en Web en una cáscara de una aplicación nativa. A los efectos de esta aplicación, la dirección URL que se ajuste a dentro del sitio es "Home/imagen" para invocar la funcionalidad nativa de cámara. Cuando el usuario se encuentra en la página de detalles de la orden de trabajo, verá un icono de cámara en la esquina superior derecha de la pantalla como se muestra en figura 8. Al hacer clic en este icono invoca la cámara nativa.

Invoking Native Camera Functionality
Figura 8 invocando la funcionalidad nativa de cámara

Windows Phone

Windows Phone utiliza Silverlight para toda la funcionalidad nativa. En cierto modo, esto hace que Windows Phone la plataforma más fácil apoyar para que el desarrollador de Web móvil que esté familiarizado con ASP.NET. El diseño básico de XAML para la shell de aplicación nativa es simple, como se muestra aquí:

<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>

Los elementos claves señalar aquí que IsScriptEnabled está establecida en true — porque, de forma predeterminada, el control del explorador Web en Windows Phone no activar secuencias de comandos — y que me manejo del evento de explorar.

En el MainPage.xaml.cs, se muestra en la figura 9, manipular el evento webBrowser1_Navigating. Si la dirección URL de exploración coincide con la dirección URL que estoy buscando, destacar el ID de la orden de trabajo que yo estoy trabajando con e invocar el nativo CameraCaptureTask mientras cancelando la navegación Web. Después de que el usuario toma la foto con la cámara, la photoCaptureOr­SelectionCompleted método es invocado. Aquí, cargar la imagen en el servidor Web mediante el mismo formulario HTTP acción puesto que sería utilizar el sitio Web si he presentado un formulario que contiene un botón de entrada de carga de archivo. Una vez finalizada la carga de la foto, se invoca upload_FormUploadCompleted, regresando al usuario al flujo de aplicación 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 tiene algunos comportamientos diferentes al interactuar con la versión basada en Web de los controles de Google Maps o Bing Maps comparado con Android o iOS. Debido a la manera del Internet Explorer 9 explorador móvil captura gestos táctiles, golpetazo y pellizco sin pasar a través del motor de JavaScript, los mapas basados en Web no zoom panorámica con gestos y debe usar el zoom o pan controles previstos por el mapa. Dada esta limitación, una mejora futura de este proyecto sería invocar el control nativo de Bing Maps en Windows Phone donde se requiere funcionalidad de mapa interactivo y, a continuación, volver a la aplicación Web en pantallas que no requieran funcionalidad de mapa interactivo.

Android

El código Java para Android fue escrito por mi colega, Sean Christmann y es similar al código para Windows Phone. El siguiente esquema XML define un diseño de pantalla completa para el control de vista Web 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>

En EffectiveUIActivity.java, se muestra en la figura 10, el reemplazo de onCreate establece la WebViewClient para reemplazar el onLoadResource y métodos de shouldOverrideUrlLoading del control WebView para buscar la misma cadena coincidente como en Windows Phone y, si encuentra, crea la actividad de la cámara y cancela la navegación. El código también reemplaza onGeolocationPermissionsShowPrompt para suprimir el mensaje al usuario que ocurriría cada vez que se ejecute la aplicación para permitir el permiso para el control de vista Web para acceder a la ubicación GPS. Después de ejecuta la actividad de la cámara, la función de onActivityResult puestos la imagen al servidor Web utilizando el mismo método que en el anterior ejemplo de Windows Phone y, a continuación, devuelve el usuario al flujo de aplicación Web.

Figura 10 Android 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

El código Objective-C para iOS también fue escrito por mi colega, Sean Christmann, y es también similar a la utilizada para Windows Phone y Android. En WebCameraViewController.m se muestra en la
figura 11, el control de UIWebView ejecuta el método shouldStartLoadWithRequest para realizar la coincidencia en la dirección URL solicitada. Si coincide con la cadena de dirección URL, el código devuelve "NO" para cancelar la navegación e invoca el nativo UIImagePickerController. Esto permite al usuario elegir una imagen de la Fototeca o tomar una nueva imagen con la cámara incorporada. Después de seleccionar la imagen, el código puestos luego de la imagen al servidor Web utilizando la biblioteca de ASIFormDataRequest (allseeing-i.com/ASIHTTPRequest) antes de regresar a la UIWebView al flujo normal de aplicación.

Figura 11 iOS código

- (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;
    }
}

Degradación agraciada de la experiencia móvil

Así que ¿qué ocurre si el usuario del sitio Web móvil no está utilizando el shell de aplicación nativa para acceder a la cámara? En este escenario, es importante contar con una elegante degradación de la experiencia del usuario. Degradación agraciada es el concepto de construcción de la aplicación para que sigue para que funcione correctamente incluso si se ve con el software menos óptima. Esto no significa que cada función funciona exactamente del mismo modo o que incluso parece similar a la experiencia deseada, pero pretende garantizar que todos los objetivos fundamentales del usuario pueden todavía lograr incluso si el usuario no está recibiendo la mejor experiencia.

Para habilitar la degradación agraciada en esta aplicación, construí una página Web ASP.NET MVC 3 controlador y vista la URL de captura de imagen, "Home/imagen," que está siendo capturado por los proyectiles de aplicación nativa para proporcionar un archivo simple subir formulario como se muestra en figura 12. Este formulario permite a los usuarios que no están utilizando los depósitos móviles mejorados para realizar la misma tarea de agregar una imagen a una orden de trabajo, aunque ellos no están obteniendo la experiencia integrada. El formulario puestos a la misma acción de controlador utilizada por los proyectiles de aplicaciones nativas, fomentar la reutilización de código entre todas las diferentes plataformas y puntos de vista.

A Simple File Upload Form for Graceful Degradation
Figura 12 Simple archivo subir forma grácil degradación

Ventajas significativas de costos

El enfoque de aplicación híbrida puede proporcionar ventajas de costos significativos sobre aplicaciones nativas únicas, tanto en el corto y largo plazo. Herramientas tales como jQuery Mobile reducir las diferencias de uso, que pueden conducir a ventajas importantes para el negocio donde no requiere acceso de dispositivos nativos.

Con dispositivos móviles proliferando como reguero de pólvora, tienes pocas opciones cuando buscando para construir una aplicación móvil:

  • Generar una aplicación nativa para cada plataforma que desee apoyar esto tiene la ventaja de proporcionar la mejor experiencia de usuario y rendimiento para cada plataforma, permitiendo el acceso a todas las funciones nativas del dispositivo y el poder de mercadeo de las tiendas app. Sin embargo, la desventaja es que podría ser significativamente más caro construir y mantener porque requerirá codebase separado para cada plataforma que desea apoyar. Además, cada nueva versión de la aplicación requiere que la aplicación volver a presentarse a las tiendas app.
  • Construir un esta aplicación de Mobile Web tiene la ventaja de ser la opción más simple y barata para desarrollar, lanzar y actualización para todas las plataformas, pero la experiencia del usuario puede verse comprometida por la falta de acceso a las características de hardware nativo. Falta de acceso a las tiendas de aplicaciones también puede comprometer adopción de su aplicación, empujando a todos de la comercialización de su aplicación a usted.
  • Construir un híbrido nativo y Mobile Web Application este es el enfoque comentamos, que proporciona un sólido compromiso entre los altos costos de desarrollo de una aplicación nativa única para cada plataforma y la falta de acceso al hardware nativo para una aplicación Web móvil. Esta opción también proporciona acceso a las tiendas de aplicaciones, aumentando el alcance de su aplicación.

Es importante señalar que ninguno de estos métodos es intrínsecamente mejor que los demás, todos ellos tienen sus propias fortalezas y debilidades. Un análisis de costo-beneficio integral de cada una de las opciones ayudará a determinar qué camino es el correcto para los usuarios y su negocio. Al tomar esta decisión, es importante tener en cuenta la experiencia del usuario, los costos de desarrollo inicial y los costos de mantenimiento, así como más sutiles factores tales como la aprobación de comercialización y usuario.

Para muchos escenarios de aplicaciones de negocio, abogo para la inclusión de una Web móvil o aplicación híbrida, como el esfuerzo adicional de crear aplicaciones nativas únicas para cada plataforma móvil podría superar los beneficios empresariales. Los escenarios de negocios deben revisarse cuidadosamente dentro de los confines de una implementación de aplicaciones Web o híbridos móvil.

Las aplicaciones móviles están aquí para quedarse y son cada vez más importantes como informática desplazamientos fuera de la experiencia de escritorio tradicional a una variedad de experiencias móviles. Como ves para crear aplicaciones en el espacio móvil, recuerde que el compromiso no siempre una palabra sucia y que puede resultar en el mejor de ambos mundos.

Shane Church es un líder técnico de EffectiveUI en Denver, Colorado Él ha venido desarrollando en Microsoft.NET Framework con un enfoque en ASP.NET y Microsoft tecnologías móviles desde 2002. Se encuentra en su blog s church.net. Puede aprender más acerca de EffectiveUI en effectiveui.com.

Gracias al siguiente experto técnico por su ayuda en la revisión de este artículo: Dr.James McCaffrey