#199 Mobile Devices
- Download:
- source codeProject Files in Zip (198 KB)
- mp4Full Size H.264 Video (22.4 MB)
- m4vSmaller H.264 Video (14.4 MB)
- webmFull Size VP8 Video (38.2 MB)
- ogvFull Size Theora Video (28.5 MB)
Sta diventando sempre più comune navigare i siti web con dispositivi mobili tipo gli smartphone. Rispetto ai desktop e i laptop, lo schermo di questi dispositivi è più piccolo, per cui è opportuno controllare la vostra applicazione per vedere come si comporta in questi casi d’uso.
Il modo migliore per farlo, è controllare la vostra applicazione su di un dispositivo reale. Ovviamente “localhost” non andrà più bene come indirizzo su di un dispositivo mobile (dove non è deployata l’applicazione), per cui occorrerà inserire o l’indirizzo IP della macchina server della vostra applicazione oppure il suo nome locale al dominio, assumendo che il dispositivo mobile si trovi nella stessa rete locale. Per esempio la macchina su cui si trova l’applicazione Rails che testeremo in questo episodio si chiama noonoo, per cui possiamo raggiungere l’applicazione da qualunque altra macchina nella rete, puntando a http://noonoo.local:3000/
.
Se non avete a disposizione un dispositivo reale potete scaricare un simulatore del dispositivo per provare. Se voleste testare l’applicazione su un iPhone, il simulatore è disponibile sull’Apple’s Developer site. Analogamente, un emulatore viene fornito come parte integrante del Palm Pré SDK.
Per testare su un iPhone, un’altra alternativa è iPhoney. Questo progettino fornisce una emulazione quasi esatta di un iPhone, pur non essendo preciso come il simulatore originale Apple, ma è almeno più leggero da scaricare che non l’intero SDK dell’iPhone. Se usate iPhony per testare un sito che prevede comportamenti differenti in base ai singoli dispositivi mobili, ricordatevi di configurare il corretto user agent dal menu di iPhoney, per vedere visualizzato il contenuto corretto:
La nostra applicazione che gira su iPhoney.
L’aspetto di questa applicazione può decisamente essere migliorato per essere visualizzato su di un dispositivo mobile ed ora dedicheremo il resto dell’episodio proprio a questo scopo.
Tanto per cominciare, aggiungiamo un foglio di stile al layout della nostra applicazione, da includersi solo se l’applicazione è mostrata su di un dispositivo mobile:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title><%= h(yield(:title) || "Untitled") %></title> <%= stylesheet_link_tag 'application' %> <%= stylesheet_link_tag 'mobile' if mobile_device? %> <%= yield(:head) %> </head> <body> <!-- content omitted --> </body> </html>
Il nuovo foglio di stile per mobile sarà incluso solo se un metodo chiamato mobile_device?
restituisce true
, e ora dobbiamo scrivere quel metodo. Potremmo usare l’attributo media
che ha l’elemento link
per discriminare i dispositivi che dovrebbero usare lo stile, ma i browser per il mobile potrebbero interpretare questo attributo in maniera diversa. Usando il nostro metodo per determinare se includere o meno lo stile, possiamo controllare per quale dispositivo includere lo stile, semplicemente leggendo la string user agent inviata con la richiesta dal browser.
Il nostro prossimo obiettivo è quello di scrivere il metodo mobile_device?
. Lo mettiamo nell’application controller in modo tale che sia accessibile da tutti gli altri controller (per ereditarietà):
class ApplicationController < ActionController::Base helper :all protect_from_forgery private def mobile_device? request.user_agent =~ /Mobile|webOS/ end helper_method :mobile_device? end
Dentro al metodo controlliamo lo user agent della richiesta per vedere se contiene o la parola “Mobile”, che fa match sia con l’iPhone che con l’Android, oppure la parola “webOS”, che fa match col Palm Pré. Naturalmente, si può personalizzare l’espressione regolare per intercettare qualunque dispositivo al quale vogliate mostrare un foglio di stile per mobile. Infine, segnamo il metodo come un metodo helper, così che lo possiamo accedere dalle viste. Se volete discriminare uno specifico dispositivo, esiste una corposa lista di id relativi agli user agent dei dispositivi mobili tuttora disponibili.
Ora che abbiamo scritto il metodo helper, creiamo il foglio di stile per i dispositivi mobili che useremo.
body { background-color: #FFF; } #container { width: 90%; min-width: none; margin: 0 auto; background-color: #FFF; padding: 0; border: none; margin-top: 20px; }
Per vedere come appare la pagina con il foglio di stile mobile, possiamo fare uso della funzionalità di Safari di cambio di user agent. Sotto il menu Sviluppo > User Agent
cè una lista di stringhe di user agent che possono essere scelte, incluso quelle riferite alle varie versioni di Mobile Safari. Se scegliamo una di queste e riguardiamo la nostra applicazione, vediamo che la pagina ha ora lo stile mobile applicato:
Alternando fra i vari stili
Ora che abbiamo il nostro metodo helper mobile_device?
, possiamo usarlo per cambiare l’aspetto del sito a seconda del dispositivo che lo si stia vedendo da un dispositivo mobile. Una cosa che aggiungiamo è il collegamento per permettere agli utenti di cambiare l’aspetto del sito dalla versione mobile a quella standard. Per fare ciò, modifichiamo il file di layout della nostra applicazione aggiungendo il seguente codice in cima al body
:
<p> <% if mobile_device? %> <%= link_to "Full Site", :mobile => 0 %> <% else %> <%= link_to "Mobile Site", :mobile => 1 %> <% end %> </p>
Questo codice mostra un link al sito standard se al momento si sta vedendo la versione mobile e viceversa. Il link ridirige alla pagina che si sta attualmente guardando, con un parametro di query chiamato mobile
che determina quale versione del sito mostrare.
Nell’application controller possiamo impostare un before_filter
che setti una variabile di sessione in modo tale che, una volta cliccato sul link, la versione scelta continui ad essere visualizzate durante la navigazione del sito da parte dell’utente. Il before_filter
imposta una variabile di sessione se c’è un parametro mobile nella stringa della query. Modifichiamo anche il nostro metodo mobile_device?
in modo tale che controlli per vedere se quella variabile di sessione esiste e, nel caso, decida quale versione del sito mostrare, a seconda del suo valore. Se la variabile di sessione non è stata impostata, decidiamo in base alla stringa user_agent:
class ApplicationController < ActionController::Base helper :all protect_from_forgery before_filter :prepare_for_mobile private def mobile_device? if session[:mobile_param] session[:mobile_param] == "1" else request.user_agent =~ /Mobile|webOS/ end end helper_method :mobile_device? def prepare_for_mobile session[:mobile_param] = params[:mobile] if params[:mobile] end end
Se ricarichiamo la pagina ora, ci sarà un link alla versione completa del sito e se ci clicchiamo sopra vedremo la versione standard, anche se stiamo guardando la pagina da un browser il cui user agent contiene la parola chiave “mobile”.
Questa scelta rimarrà salvata, in modo tale che qualsiasi link si segua successivamente, le pagine siano sempre presentate con il formato standard deciso una volta per tutte.
Viste distinte per dispositivi mobili
Ciò che abbiamo fatto fina ad ora funzionerà per i casi in cui vogliamo dare un aspetto decente all’applicazione per i dispositivi mobili, ma come potremmo fare, avendo dei progetti più ambiziosi, per far sì che l’applicazione appaia e si comporti più come una applicazione nativa che non come un sito web, quando è vista da un dispositivo mobile? Per perseguire questo intento, dovremo cambiare verosimilmente tutte le viste nella nostra applicazione. In che modo?
Il trucco per fare ciò è di creare un nuovo MIME type nella nostra applicazione. Rails fornisce un file per fare proprio questa cosa, il /config/initializers/mime_types.rb
. Il file contiene un esempio commentato per fornire un nuovo tipo per iphone
, che possiamo modificare per adattare ad un qualunque altro dispositivo mobile
. Questa modifica ci da un formato HTML alternativo per i dispositivi mobile:
# Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register_alias "text/html", :mobile
Comunque dobbiamo ancora impostare quel MIME type, e per fare ciò dobbiamo torniamo indietro alla before_filter
nel nostro application controller e impostare il formato a :mobile
se si sta guardando la versione mobiledel sito:
def prepare_for_mobile session[:mobile_param] = params[:mobile] if params[:mobile] format :mobile if mobile_device? end
Ora che il MIME type è impostato, possiamo usarlo nelle nostre actions dei controller per cambiare il comportamento di ciascuna, a seconda del tipo, usando respond_to
, come mostriamo nella action index
del controller dei progetti:
def index @projects = Project.all respond_to do |format| format.html format.mobile end end
Il blocco respond_to
non è necessario, comunque, se stiamo solamente fornendo una vista alternativa basata sul formato. In quel caso, occorre solamente fornire un nuovo template col nome del formato prescelto al posto di html. Per la vista index di cui sopra, creiamo un file chiamato /app/views/projects/index.mobile.erb
e, giusto per cominciare, lo compiliamo con semplice testo:
This is a mobile version!
Se visitiamo la versione mobile di quella pagina ora, vediamo come appare:
Ora che questo file è a posto, possiamo creare una UI che assomigli di più ad un’applicazione nativa per mobile. Ci sono un paio di librerie che possiamo usare per rendere tutto più semplice: iui e jQTouch: in questo episodio usiamo jQTouch. jQTouch semplifica notevolmente la creazione di applicazioni web che appaiano e si comportino come una applicazione nativa per iPhone.
Una volta scaricato e scompattato, jQTouch dovrebbe avere una struttura del genere:
Per rendere ulteriormente più semplice jQTouch, copiamo le cartelle extensions e themes sotto alla cartella jqtouch
nella cartella public
della nostra applicazione.
Poi creiamo un nuovo file di layout per la versione mobile della nostra applicazione:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title><%= h(yield(:title) || "Untitled") %></title> <%= stylesheet_link_tag "/jqtouch/jqtouch.min.css", "/jqtouch/themes/apple/theme.min.css" %> <%= javascript_include_tag "/jqtouch/jquery.1.3.2.min.js", "/jqtouch/jqtouch.min.js", "mobile" %> <%= yield(:head) %> </head> <body> <div class="current"> <%- if show_title? -%> <div class="toolbar"> <%= link_to "Back", nil, :class => "back" unless current_page? root_path %> <h1><%=h yield(:title) %></h1> <%= yield(:toolbar) %> </div> <%- end -%> <% unless flash.empty? %> <div class="info"> <%- flash.each do |name, msg| -%> <%= content_tag :div, msg, :id => "flash_#{name}" %> <%- end -%> </div> <% end %> <%= yield %> </div> </body> </html>
In questo file di layout includiamo un paio di CSS forniti da jQTouch oltre che il referimento alle librerie javascript di jQuery
e di jQTouch
. Ci mettiamo anche un riferimento ad un file, mobile.js
, che ci apprestiamo a creare. Nel layout, il contenuto è completamente racchiuso in un div
con una classe associata current
. Se la pagina deve mostrare un titolo, lo metteremo all’interno di un altro div
con classe toolbar. Analogamente, il testo flash da mostrare andrà inserito in un div
di classe info
. Sotto a tutto ciò c’è lo statement yield
che conterrà il contenuto effettivo della pagina.
Ora dobbiamo creare il file mobile.js
citato poc’anzi. Occorre solamente inserirvi all’interno una chiamata alla funzione initialiser di jQTouch:
$.jQTouch({});
La funzione initialiser accetta un hash di opzioni come parametro, ma a noi non interessa impostare alcuna opzione. Quando ricarichiamo la pagina mobile dei progetti, vedremo gli stili jQTouch applicati, ma la pagina apparirà piuttosto bruttina, dal momento che non abbiamo alcun contenuto nel template della pagina:
Se ora ritorniamo al codice della vista mobile, possiamo sotituire il codice placeholder con del codice che mostri una lista di tutti i progetti e un conteggio del numero di task per ciascuno di essi e un link per la creazione di nuovi progetti:
<% title "Projects" %> <ul> <% for project in @projects %> <li class="arrow"> <%= link_to h(project.name), project %> <small class="counter"><%= project.tasks.size %></small> </li> <% end %> </ul> <ul><li class="arrow"><%= link_to "New Project", new_project_path %></li></ul>
Reloading the page again we’ll see a nice interface that looks a lot like a native mobile application.
Ovviamente l’interfaccia sarà piuttosto limitata se vista da un vero iPhone, ma funziona bene tanto quanto la versione desktop.
Ogni vista della nostra applicazione avrà bisogno ovviamente della sua versione mobile. C’è troppo codice perchè possa essere riportato qui, ma potete scaricare i file dalla pagina di Github di Ryan Bates. Una volta fatto ciò, avremo una versione della nostra applicazione perfettamente funzionante anche su dispositivi iPhone con tanto di look nativo.
Benchè ora la versione mobile del nostro sito appaia molto migliore rispetto a prima, abbiamo perso la possibilità di passare alla visualizzazione standard del sito. Aggiungiamo un pulsante sul lato destro della toolbar di testata per riabilitare questa funzionalità:
La toolbar è definita nel file di layout della versione mobile e aggiungervi un nuovo elemento è semplice, dal momento che tutti gli elementi jQTouch sono definiti con tag HTML. Possiamo dunque aggiungere un nuovo pulsante, creando un nuovo link nel div della toolbar e dandogli come classe button
:
<div class="toolbar"> <%= link_to "Back", nil, :class => "back" unless current_page? root_path %> <h1><%=h yield(:title) %></h1> <%= link_to "Full Site", root_url(:mobile => 0), :class => "button", :rel => "external" %> <%= yield(:toolbar) %> </div>
Dobbiamo anche fornire un attributo rel
con valore external
, in modo tale che jQTouch tratti il link come collegamento ad un altro sito. Se non facessimo ciò, farebbe una richiesta AJAX che non sarebbe ciò che vorremmo.
Ricaricando la pagina un’ultima volta, possiamo ora vedere anche il nuovo pulsante su ogni pagina che permette di passare alla versione "full" del sito: