#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)
C'est devenu de plus en plus courant de naviguer sur des sites en utilisant des appareils mobiles, comme les smartphones. Comparés aux ordinateurs portables ou de bureau, ces machines ont de plus petits écrans et des fonctionalités plus limitées, et ayant cela en tête, il est important de vérifier vos applications web sur ces petits appareils pour voir comment ils réagissent.
La meilleur façon de le faire est de tester concrètement votre application sur un appareil. De toute évidence,“localhost” ne fonctionnera pas sur un mobile, et vous devrez donc utiliser soit l'adresse IP de votre machine sur laquelle tourne votre application soit sur votre domaine local, en présumant que le mobile soit aussi sur votre réseau local. Par exemple, la machine sur laquelle tourne l'application que l'on va utiliser dans cet épisode s'appelle noonoo, si bien que l'on va y avoir accès sur le réseau en tapant l'adresse http://noonoo.local:3000/
.
Si vous ne pouvez pas y accéder physiquement, vous serez obligé de télécharger un simulateur pour l'appareil, pour lequel vous voulez tester votre application. Si vous voulez tester sur un iPhone, alors un simulateur est disponible sur le site d'Apple pour les développeurs. De la mêle manière, un émulateur fait partie du SDK Palm Pré.
Pour tester sur l'iPhone, une autre alternative est iPhoney. Il fournit presqu'exactement la même émulation qu'un iPhone, même s'il n'est pas aussi fidèle que le simulateur Apple, sauf qu'il n'est pas aussi lourd à télécharger que le SDK iPhone. Si vous avez prévu de tester un site qui devra avoir des comportements différents pour les mobiles, alors il ne faudra pas oublier de définir le user agent correctement dans le menu iPhoney, de telle sorte que le contenu approprié soit bien affiché.
Notre application tournant sur iPhoney.
L'apparence de l'application peut de tout évidence être améliorée pour l'affichage sur un mobile, et c'est que nous allons faire tout au long de cet épisode.
Commençons par ajouter une feuille de style (stylesheet) dans la layout de notre application, qui ne sera incluse que si notre application est visitée par un 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>
La nouvelle feuille de style mobile ne sera incluse que si une méthode appelée mobile_device?
retourne true
, et pour cela nous allons écrire cette méthode. On aurait pu aussi utiliser l'attribut media
de la balise link
pour restreindre aux mobiles l'utilisation de cette feuille de style, mais les navigateurs mobiles pourraient interpréter cet attribut de façons fort différentes. En utilisant notre propre méthode pour déterminer s'il faut ou non inclure la feuille de style, on va avoir un meilleur contrôle sur quels appareils vont afficher cette feuille, lorsque l'on va lire la chaîne de leur user agent envoyé par leur navigateur.
La prochaine étape est d'écrire la méthode mobile_device?
. on va la placer dans l'application controller, ainsi tous les controllers pourront avoir accès à cette méthode.
class ApplicationController < ActionController::Base helper :all protect_from_forgery private def mobile_device? request.user_agent =~ /Mobile|webOS/ end helper_method :mobile_device? end
Dans cette méthode, on va vérifier la requête du user agent pour voir s'il contient soit le mot “Mobile”, correspondant aux appareils iPhone et Android, soit “webOS” correspondant au Palm Pré. Vous pouvez, bien sûr, customiser cette expression régulière pour cibler quels appareils doivent afficher la feuille de style mobile. Au final on désigne cette méthode comme une helper méthode, de telle sorte qu'on pourra y avoir accès dans toutes nos vues. Si vous décidez de cibler un appareil spécifique, il existe une liste complète d'agents pour appareils mobiles.
Maintenant que l'on a écrit notre helper méthode, on va écrire la feuille de style mobile, que les mobiles vont utiliser.
body { background-color: #FFF; } #container { width: 90%; min-width: none; margin: 0 auto; background-color: #FFF; padding: 0; border: none; margin-top: 20px; }
Afin de voir à quoi ressemble notre page avec notre feuille de style mobile, on peut utiliser les fonctions de simulation du user-agent dans Safari. (Safari propose un menu Développeur qui est, par défaut, désactivé. Pour l'activer, il faut se rendre dans les préférences du navigateur, menu Avancé et cocher la case correspondante; pour la version anglaise, Show Develop menu in menu bar.) (NdT: il existe aussi un module pour Firefox, appelé User Agent Switcher
qui permet de changer dynamiquement son user agent, suivant différents profils.) Dans le menu Développeur > User Agent
se trouve une liste de user-agents, qui peuvent être sélectionnés, incluant plusieurs des différentes versions de Safari Mobile. Si on choisit l'une d'elles et que l'on regarde de nouveau notre application, on va voir la page avec la feuille de style mobile qui lui est appliquée.
Basculer entre les Sites
Maintenant que nous avons notre helper méthode mobile_device?
, on peut l'utiliser pour modifier le comportement du site en fonction de l'appareil qui le visite. Ce que l'on va ajouter, c'est un lien qui va permettre aux utilisateurs de basculer entre la version intégrale et la version mobile du site. Pour ce faire, on va modifier notre layout d'application en ajoutant le code suivant avant la balise body
.
<p> <% if mobile_device? %> <%= link_to "Full Site", :mobile => 0 %> <% else %> <%= link_to "Mobile Site", :mobile => 1 %> <% end %> </p>
Ce code va afficher un lien vers le site intégral si on est actuellement sur les pages mobiles et vice versa. Le lien va rediriger vers la page qui est actuellement visitée avec une chaîne de requête comme paramètre, appelée mobile, qui va déterminer quelle version du site à afficher.
Dans notre application controller, on peut définir un before_filter
qui va définir une variable de session, si bien que, dès que le lien est cliqué, cette version continuera d'être affichée tout au long de la navigation de l'utilisateur sur le site. Le before_filter
va définir une variable de session s'il y a un paramètre 'mobile' dans la chaîne de requête. On va aussi modifier notre méthode mobile_device?
de telle sorte qu'elle vérifie si cette variable existe et, auquel cas, choisira quelle version du site à afficher, en se basant sur sa valeur. Si cette valeur n'a pas été définie, on se basera sur la chaîne de requête.
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
Si on recharge la page maintenant, il va y avoir un lien vers la version intégrale du site, et lorsque l'on clique, on verra la version complète, même si on est en train de voir le site avec un user agent, dont la chaîne contient le mot “mobile”.
Cette préférence va demeurer, si bien que si l'on clique sur n'importe quel lien ci-dessus, on restera sur la version intégrale de l'application.
Vues distinctes pour les Appareils Mobiles
Tout ce que l'on a fait jusqu'à présent marchera pour les cas où l'on veut une application cousue-main pour des mobiles, mais qu'en serait-il si l'on a de plus grandes ambitions et que l'on veuille changer l'application afin qu'elle ressemble et réponde comme une application mobile native? Pour cela, il faudrait changer toutes les vues ou presque de notre application. Comment allons-nous faire?
L'astuce est de créer un nouveau type MIME dans notre application, et Rails fournit justement un fichier ad hoc pour cela: /config/initializers/mime_types.rb
. Ce fichier contient un exemple commenté pour fournir un nouveau type iphone
, que nous allons modifier pour créer notre nouveau type mobile
. Cela nous permet d'avoir un nouveau format HTML alternatif pour les appareils mobiles.
# 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
De toute façon, on doit encore définir ce type MIME, et pour ce faire, on revient dans notre before_filter
dans l'application controller et définir le format à :mobile
si c'est la version mobile qui est demandée.
def prepare_for_mobile session[:mobile_param] = params[:mobile] if params[:mobile] format :mobile if mobile_device? end
Maintenant que le type MIME est défini, on peut l'utiliser dans nos actions des controllers pour changer le comportement de chaque action selon le type en utilisant respond_to
, comme on va le démontrer dans l'action index
du controller des projects.
def index @projects = Project.all respond_to do |format| format.html format.mobile end end
Le bloc respond_to
n'est pas nécessaire, si nous pouvons fournir une vue alternative basée sur le format. Dans ce cas, on a juste besoin de fournir a nouveau template ayant comme nom celui du nouveau format,là où normallement se trouve html. Pour la vue index ci-dessus, on va créer un fichier appelé /app/views/projects/index.mobile.erb
et pour commencer, on y placer un peu de texte.
Voilà la version mobile!
Si on regarde la version mobile de cette page, on voit maintenant que la vue mobile s'affiche.
Maintenant que c'est en place, on peut créer une Interface Utilisateur (IU) qui va bien plus ressembler à une applicaion mobile native. Il existe un certain nombre de librairies qui permettent de rendre les choses plus faciles: iui et jQTouch et celle que nous allons utiliser ici est jQTouch. jQTouch facilite grandement les choses pour créer une appli web, qui ressemble à une appli iPhone.
Après avoir téléchargé et dézippé jQTouch, la structure des répertoires devrait être:
Pour faciliter l'utilisation de jQTouch, on va déplacer les répertoires extensions et themes dans le répertoire jqtouch
et ensuite faire glisser ce répertoire dans le dossier public
de notre application.
Ensuite, on va créer un nouveau fichier layout pour la version mobile de notre application.
<!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>
Dans ce fichier layout, on va inclure quelques fichiers CSS, fournis par jQTouch et dans le même temps les références JavaScript de jQuery
et de jQTouch
. On va y mettre aussi une référence au fichier mobile.js
, que l'on va créer. Dans cette layout, le contenu de la page est englobé dans une balise div
ayant comme classe current
. si cette page doit afficher un titre, alors il sera englobé dans une autre div
de classe toolbar
. Pareillement, si on doit afficher un méssage flash, il sera placé dans une autre div
, cette fois avec une classe info
. Enfin, on retourne yield
de ce qui doit être affiché dans le template courant.
ensuite, il nous faut écrire le nouveau fichier mobile.js
. Tout ce qu'on doit y mettre est un appel pour initaliser jQTouch.
$.jQTouch({});
La fonction d'initialisation prend un hash d'options, mais ici, on ne va définir aucun d'eux. Quand on va recharger de nouveau la page mobile des projets, on peut voir que les styles jQTouch sont bien appliqués, mais le rendu est assez moche, et aussi on n'y voit aucun contenu.
Si on revient au code de la vue mobile de la page, on peut remplacer le code substituable par un code qui listerait tous les projets et un compteur du nombre de tâches pour chacun des projets, et enfin un lien pour créer un nouveau projet.
<% 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>
Après avoir rechargé de nouveau la page, on va voir une belle interface qui ressemble beaucoup à une application mobile native.
Évidemment l'interface sera bien plus petite lorsqu'affichée sur un iPhone, mais cela fonctionnera de la même façon sur un navigateur d'ordinateur.
Chaque vue de notre application aura aussi besoin d'une version mobile. Cela repésente bien trop de code à vous présenter, mais vous pouvez télécharger les sources sur la page Github de Ryan Bates. Une fois que cela est fait, on a une application mobile complètment fonctionnelle, qui ressemble à une application mobile.
Alors que l'on a un bien meilleur rendu mobile de notre application, on a perdu la possibilité de basculer vers la version intégrale. On va rajouter un bouton sur le côté droit de la barre d'outils (toolbar) du haut pour réactiver cette fonctionalité.
La toolbar est definie dans la layout mobile, et lui ajouter de nouveaux contrôles est facile, dans le sens où les éléments de jQTouch sont definis par des balises HTML. On peut donc ajouter un nouveau bouton en créant un nouveau lien à l'intérieur de la div toolbar et en lui définissant une 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>
On doit aussi définir un attribut rel
ayant une valeur d'external
, ainsi jQTouch traitera ce lien comme un appel externe à un autre site. Si on ne fait pas cela, une requête AJAX sera faite, ce qui n'est pas exactement ce que l'on recherche.
Lorsqu'on recharge la page une dernière fois, on aura un bouton sur chaque page, qui pourra nous emmener sur la version intégrale du site.