Interfaces graphiques avancées Android

ActionBar

La barre d'action

Présentation

  • Barre située dans la partie haute de chaque écran
  • Améliore la navigation entre les écrans
  1. Donne une identité à une application
  2. Met en avant les actions importantes
  3. Regroupe les actions moins importantes...
    • ...dans un menu déroulant : l'action overflow
    • ...pouvant être affiché avec la touche Menu

Conception

  • Nombre d'actions en fonction de la largeur de l'écran
    • 2 actions = largeur inférieure à 360 dp (density-independent pixels)
    • 3 actions = largeur comprise entre 360-499 dp
    • 4 actions = largeur comprise entre 500-599 dp
    • 5 actions = largeur égale ou supérieure à 600 dp
  • Identifier une action importante (Frequent - Important - Typical)
    • Si l’action est fréquemment utilisée (utilisée plusieurs fois par l’utilisateur)
    • ... ou si l’action est importante (doit facilement être trouvée par l’utilisateur)
    • ... ou si l’action est importante dans des applications similaires (se serait surprenant pour l’utilisateur de trouver l’action dans l’action overflow)
    • alors il s’agit d’une action importante qui peut apparaître dans l'ActionBar
  • Utiliser des icônes standards pour les actions communes

Afficher la barre d'action

  • A partir de l'API Level 11
    • Affichée par défaut dans les activités utilisant le thème Holo (ou une extension)
  • Rétro-compatibilité jusqu'à l'API Level 7
    • Il faut utiliser le thème AppCompat (ou une extension)​​
<!-- res/values/styles.xml -->
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
    </style>
</resources>

<!-- AndroidManifest.xml -->
<application ... android:theme="@style/AppTheme">

Masquer la barre d'action

  • Ne pas dessiner la barre d'action
    • Utiliser le thème Holo.NoActionBar
  • Masquer la barre d'action
ActionBar actionBar;
if (Build.VERSION.SDK_INT >= 11) {
    actionBar = getActionBar();
} else {
    actionBar = getSupportAction();
}
actionBar.hide();
actionBar.show();

Ajout d'une action (1/2)

  • onCreateOptionsMenu() de la classe Activity est appelée pour initialiser la barre d'action

  • Modifier l'instance Menu passée en premier paramètre pour ajouter des boutons
  • La ressource Menu permet de définir des boutons d'actions dans un fichier XML (dans res/menu/)
    • Le tag <item> représente un bouton d'action
    • Attribut android:showAsAction d'un bouton
      • ifRoom : affiche le bouton si un espace est disponible
      • never : place le bouton dans l’action overflow
      • Les valeurs peuvent être combinées : ifRoom|withText affiche le bouton avec son titre dans la barre si un espace est disponible
    • Attribut android:OrderInCategory pour prioriser les bouttons

Ajout d'une action (2/2)

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu items for use in the 
    // action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(
        R.menu.main_activity_actions,
        menu
    );
    return super.onCreateOptionsMenu(menu);
}
<!-- res/menu/main_activity_actions.xml -->
<menu 
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/action_compose"
        android:icon="@drawable/ic_action_compose"
        android:title="@string/action_compose"
        android:showAsAction="ifRoom|withText"/>
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_action_search"
        android:title="@string/action_search" 
        showAsAction="never"/>
</menu>

Activité

Définition XML du menu

Traitement d'une action

  • onOptionsItemSelected() de la classe Activity est appelée lorsque l'utilisateur appuie sur un bouton d'action
  • Le paramètre d'appel MenuItem correspond au bouton pressé
  • Le bouton pressé peut être identifié via sa méthode getItemId()
  • Le traitement associé au bouton doit retourner un booléen
  • Si le bouton pressé n'est pas traité par l'activité, il est important de retourner l'exécution de la même méthode du parent
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_compose:
            composeMessage();
            return true;
        case R.id.action_search:
            openSearch();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Navigation hiérarchique (1/2)

  • L'icône de l'application (élément tout à gauche) peut être utilisé comme bouton de navigation hiérarchique entre les écrans
  • Il s'agit du bouton Up composé de l'icône de l'application précédée du caractère "<"
  • L'utilisation de Up permet de revenir à l'activité parente
  • La méthode setDisplayHomeAsUpEnabled() active le bouton Up
  • L'attribut android:parentActivity permet d'indiquer l'activité parente d'une activité (tag <activity>) dans le manifeste
  • Il faut utiliser un tag <meta-data> fournissant l'information PARENT_ACTIVITY pour assurer la rétro-compatibilité
  • Documentation du bouton de navigation hiérarchique

Navigation hiérarchique (2/2)

<!-- The main/home activity (has no parent activity) -->
<activity
  android:name="com.example.app.MainActivity"
  ... >
      ... 
</activity>

<!-- A child of the main activity -->
<activity
  android:name="com.example.app.DisplayMessageActivity"
  android:label="@string/title_activity_display_message"
  android:parentActivityName="com.example.app.MainActivity">
    <!-- Parent activity meta-data to support API level 7+ -->
    <meta-data 
      android:name="android.support.PARENT_ACTIVITY"
      android:value="com.example.app.MainActivity" />
</activity>

AndroidManifest.xml

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_details);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

DisplayMessageActivity.java

Apparence

  • Icône de l'application
    • Utiliser l'attribut android:logo dans le tag <activity> pour remplacer l'icône de l'application utilisé par défaut
  • Fractionner la barre d'action
    • Consiste à décharger les boutons d'actions dans une
      barre positionné dans la partie basse sur les petits écrans
    • Utiliser l'attribut android:uiOptions avec la valeur
      splitActionBarWhenNarrow dans le tag <activity>
    • Utiliser un tag <meta-data> fournissant l'information
      UI_OPTIONS pour assurer la rétro-compatibilité
  • Personnaliser les couleurs
    • Un style peut être défini dans le fichier styles.xml
    • Documentation sur les styles et thèmes

Fragment

Interfaces multi-panneaux

Présentation

  • Introduit par l'API Level 11 pour améliorer la conception d'interfaces pour les grands écrans
  • Brique pouvant être intégrée dans une ou plusieurs activités
  • Une activité peut combiner plusieurs fragments pour créer des interfaces multi-panneaux
  • Un fragment a son propre cycle de vie et reçoit ses propres événements déclenchés par utilisateur
  • Le cycle de vie d'une activité impacte ceux des fragments utilisés
  • Un fragment partage des informations avec l'activité dans laquelle il est intégré
  • Un fragment définit son propre affichage dans une activité et peut agir sur la barre d'action
  • Un système de transaction permet d'annuler plusieurs modifications d'un fragment avec la touche Back

Conception

  • Un appareil grand écran dispose d'assez d'espace pour combiner plusieurs écrans d'un petit appareil
  • Un fragment doit être conçu comme une sous-activité réutilisable dans différentes activités
  • Un fragment doit gérer...
    • son propre cycle de vie
    • son affichage
    • et les événements utilisateur
  • Un fragment doit pouvoir
    être utilisé par un autre
    fragment
    pour éviter une
    implémentation complexe
    des activités

Cycle de vie (1/3)

  • Proche de celui de l'activité (cela facilite la création d'un fragment à partir d'une activité n'en utilisant pas)
  • Certaines méthodes associées au cycle de vie du fragment doivent obligatoirement être définies
    • onCreate() appelée à la création du fragment pour initialiser les principaux composants
    • onCreateView() appelée lorsque le fragment doit dessiner l'interface et retourne une View
    • onPause() appelée quand l'utilisateur quitte le fragment (utilisée pour valider des changements à conserver)
  • Les autres méthodes associées au cycle de vie du fragment permettent de définir un traitement spécifique à chaque étape
  • Le cycle de vie d'un fragment est impacté par celui de l'activité dans lequel il est utilisé

Cycle de vie (2/3)

  • Un fragment a 3 états : Resumed quand il est visible, Stopped s'il ne l'est plus et Paused s'il est partiellement recouvert
  • L'état d'un fragment peut être conservé (comme celui d'une activité) via le premier paramètre Bundle de onCreate()
  • Le cycle de vie du fragment a plus d'étapes que celui de l'activité

Cycle de vie (3/3)

Création d'un fragment

Ajout d'un fragment

FragmentTransaction T = getFragmentManager()
                          .beginTransaction();
ExampleFragment F = new ExampleFragment();
T.add(R.id.example_container, F);
T.commit();
<LinearLayout
    xmlns:android="..."
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:name="com.example.ExampleFragment"
        android:id="@+id/example_container"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

</LinearLayout>

Définition dans le code

Définition XML dans un layout

Communication

  • L'activité doit gérer l'échange d'information entre ses fragments
  • Illustration  : un fragment A informe l'activité de la sélection d'un item qui le transmet à un fragment B pour l'afficher
  • L'activité peut accéder a ses fragments en utilisant la méthode getFragmentManager() puis findFragmentById()
  • La méthode getActivity() du fragment permet d'accéder à l'activité à laquelle il est associé
  • Mécanisme d'échange d'événements entre fragments
    • Créer une interface dans le fragment A qui définit des callbacks
    • L'activité utilisant le fragment A doit implémenter son interface de callbacks pour interagir avec d'autres fragments
    • Le fragment A doit vérifier que l'activité l'utilisant implémente bien son interface de callbacks


public static class FragmentA extends ListFragment {

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
      public void onArticleSelected(Uri articleUri);
    }

    private OnArticleSelectedListener mListener;

    // ...

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // Activity containing this fragment must 
        // implement its callbacks
        if (!(activity instanceof OnArticleSelectedListener)) {
            throw new ClassCastException ("Activity must "
                +"implement fragment's callbacks.");
        }
        // will be used to share events with activity 
        // by calling callbacks
        mListener = (OnArticleSelectedListener) activity;
    }

    @Override
    public void onListItemClick
        (ListView l, View v, int position, long id) {
        // Append the clicked item's row ID 
        // with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId
                          (ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }

    // ...

}


public class ArticleActivity extends Activity 
    implements FragmentA.OnArticleSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_article);
    }

    @Override
    public void onItemSelected(String id) {
        // Show the detail view in this activity by 
        // adding or replacing the detail fragment using a 
        // fragment transaction. 
        Bundle arguments = new Bundle();
        arguments.putString(ArticleColumns.CONTENT_URL, id);
        FragmentB fragment = new FragmentB();
        fragment.setArguments(arguments);
        getFragmentManager()
            .beginTransaction()
                .replace(
                    R.id.article_detail_container,
                    fragment
                )
                .commit();
    }

    // ...

}

ArticleActivity.java

FragmentA.java

Agir sur la barre d'action

  • Un fragment peut contribuer à la barre d'action
  • setHasOptionMenu() indique à la création du fragment qu'il peut créer des boutons dans la barre d'action
  • Surcharger onCreateOptionMenu() pour modifier le menu passé en paramètre et y insérer les boutons
  • Les boutons sont ajoutés dans l'action overflow par défaut
  • Utiliser l'attribut android:showAsAction avec ifRoom pour afficher le bouton dans la barre si l'espace le permet
  • Lorsqu'un bouton de la barre d'action est enclenché...

Ressources

Le dossier des ressources

Introduction

  • Une application Android peut être exécutée sur une multitude d'appareils mobiles
    • Les caractéristiques matérielles sont propres à chaque type d'appareil
    • Des appareils d'un même type peuvent exécuter une version différente d'Android
  • L'exécution d'une application nécessite différentes ressources selon l'appareil utilisé
  • La manière d'organiser les ressources permet d'adapter l'application selon l'appareil d'exécution
  • Les ressources du projet d'une application sont  placées dans le dossier res/

Présentation

Répertoire Type
res/animator Descriptions d'animations au format XML
res/color Couleurs utilisées en fonction de l'état d'un widget
res/drawable Images matricielles (GIF, JPEG, PNG) ou descriptions au format XML
res/layout Descriptions de parties de l'interface graphique au format XML
res/menu Descriptions de menus de l'application au format XML
res/raw Fichiers de données brutes (musiques, pages HTML, etc)
res/values Différentes variables définies en XML (chaînes, booléens, dimensions, styles, ...)
res/xml Divers fichiers XML utilisées par l'application

Permet d'appliquer une couleur à un widget en fonction de son état

  • Fichiers : res/color/filename.xml
  • Classe correspondante : ColorStateList
  • Références :
    • Java : R.color.filename
    • XML : @color/filename
<elector xmlns:android="...">
    <!-- pressed -->
    <item android:state_pressed="true"
          android:color="#ffff0000"/>
    <!-- focused -->
    <item android:state_focused="true"
          android:color="#ff0000ff"/>
    <!-- default -->
    <item android:color="#ff000000"/>
</selector>
<Button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/button_text"
    android:textColor="@color/button_text" />

res/color/button_text.xml

Dans un fichier de layout XML

Android supporte de nombreux types d'images. Ici nous nous intéressons aux images Bitmap

  • Fichiers : res/drawable/filename.{gif,jpg,png}
  • Classe correspondante : BitmapDrawable
  • Références :
    • Java : R.drawable.filename
    • XML : @drawable/filename
Resources res = getResources();
Drawable drawable = 
    res.getDrawable(R.drawable.filename);
<ImageView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:src="@drawable/filename" />

Java

XML

Différents types de menus peuvent être définis en XML et transformés en Java avec un MenuInflater

  • Fichiers : res/menu/filename.xml
  • Classe correspondante : Menu
  • Références :
    • Java : R.menu.filename
    • XML : @menu/filename
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.example_menu, menu);
    return true;
}

public void onGroupItemClick(MenuItem item) {
    // One of the group items was clicked
    // The item parameter passed here 
    //   indicates which item it is
    // All other menu item clicks are
    //   handled by onOptionsItemSelected()
}

public void onOptionsItemSelected(MenuItem item) {
    // TODO
}
<menu xmlns:android="...">
    <group android:id="@+id/group">
        <item android:id="@+id/group_item1"
              android:onClick="onGroupItemClick"
              android:title="@string/group_item1"
              android:icon="@drawable/group_item1_icon" />
        <item android:id="@+id/group_item2"
              android:onClick="onGroupItemClick"
              android:title="@string/group_item2"
              android:icon="@drawable/group_item2_icon" />
    </group>
    <item android:id="@+id/submenu"
          android:title="@string/submenu_title"
          android:showAsAction="ifRoom|withText" >
        <menu>
            <item android:id="@+id/submenu_item1"
                  android:title="@string/submenu_item1" />
        </menu>
    </item>
</menu>

Code Java

res/menu/example_menu.xml

Android préconise la déclaration de chaînes de caractères dans des fichiers XML.

  • Fichiers : res/values/filename.xml
  • Classe correspondante : String
  • Références :
    • Java : R.string.string_name
    • XML : @string/string_name
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Dans un fichier de layout XML

res/values/strings.xml

Android supporte des tableaux et des mots pluriels

Un style définit le "look" de l'interface graphique. Il peut s'appliquer à l'application, une activité ou un widget

  • Fichiers : res/values/filename.xml
  • Classe correspondante : String
  • Références :
    • XML : @style/style_name
<?xml version="1.0" encoding="utf-8"?>
<EditText
    style="@style/CustomText"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Hello, World!" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomText" parent="@style/Text">
        <item name="android:textSize">20sp</item>
        <item name="android:textColor">#008</item>
    </style>
</resources>

Dans un fichier de layout XML

res/values/styles.xml

Ressources alternatives

  • La manière d'organiser permet de cibler des ressources pour un type précis d'appareil
  • Possibilité de choisir le layout pour une activité en fonction de la largeur d'écran
  • Il faut utiliser les qualificateurs 
    pour nommer les ressources
  • Les qualificateurs peuvent également être utilisés pour nommer les dossiers
  • Android supporte de nombreux qualificateurs
    • Langue et région : en, fr, en-rUS, fr-rFR, fr-rCA, etc
    • Taille écran : small (320x426), normal (320x470), large (480x640), xlarge (720x960)
    • Largeur et hauteur : w<N>dp et h<N>dp
    • Orientation : port (verticale) et land (horizontale)
    • Version de l'API : v<N>, v3, v4, v7, ..., v21

Material Design

Nouvelle spécification

Présentation

  • Nouvelle spécification publiée par Google
  • Les exigences décrites ont pour objectif de produire des interfaces homogènes visuellement
  • Le nouveau thème Material introduit par l'API Level 11 respecte la spécification
  • Material succède à Holo en place depuis 2011
  • Android épouse la mode des designs flats
  • Le thème rend les interfaces plus intuitives et ergonomiques : l'expérience utilisateur est améliorée

Conception

  • Une interface Material Design est conçue comme un ensemble de cartes flottantes sur un arrière plan
  • Une carte occupe une position dans l'espace
  • La notion de "niveau" simule l'axe Z grâce aux ombres
  • Nouvelle méthode setElevation() de la classe View pour définir le niveau d'élévation
  • L'attribut android:elevation permet de définir le niveau d'élévation dans le XML
  • Les éléments d'une interface Material Design ne doivent jamais se "téléporter" mais "transiter"

Utilisation

  • Préciser le SDK 21 comme version minimum dans le manifeste
  • Déclarer l'utilisation du thème Material
<resources>
    <!-- Etendre le thème Material ou l'une de ses extensions -->
    <style name="AppTheme" parent="android:Theme.Material">
        <!-- Personnalisations du thème -->
    </style>
</resources>
  • Thème Material et extensions
    • Material : version sombre
    • Material.Light : version claire
    • Material.Light.DarkActionBar : version claire avec la barre d'action sombre
  • Personnaliser la palette de couleur du thème Material
<!-- Couleur de l'ActionBar -->
<item name="android:colorPrimary">@color/primary</item>
<!-- Couleur de la StatusBar -->
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<!-- Couleur des éléments de formulaire (zones de texte, checkboxes, ...)-->
<item name="android:colorAccent">@color/accent</item>

RecyclerView (1/2)

RecyclerView (2/2)

public class MyActivity extends Activity {

    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);
        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);
    }
    // ...
}

CardView (1/2)

  • Afficher des informations dans un cadre
  • Offre un bon rendu sur toutes les plateformes
  • Apparence personnalisable
  • Positionnée sur un niveau avec setElevation()
    (ombres créées sur les éléments inférieurs)
  • Projet CardView sample

CardView (2/2)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto">

    <!-- A CardView that contains a TextView -->
    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_gravity="center"
        android:layout_width="200dp"
        android:layout_height="200dp"
        card_view:cardCornerRadius="4dp">

        <TextView
            android:id="@+id/info_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v7.widget.CardView>

</LinearLayout>

Rétro-compatibilité (1/2)

  • Material Design uniquement disponibles à partir de l'API Level 21
  • Possibilité d'assurer la rétro-compatibilité jusqu'à l'API 7
  • Si le thème Material est utilisé dans res/values/styles.xml : l'application est uniquement utilisable sur Android 5
  • Préférer déclarer un ancien thème qui sera écrasé par le nouveau thème Material dans res/values-v21/styles.xml
  • Même remarque pour les layouts : déclarer des layouts spécifiques à Material Design dans res/layout-v21
  • Le composant v7 Support Librairies r21 permet d'assurer la rétro-compatibilité de certains éléments
    • Thème AppCompat pour simuler Material
    • Introduit les classes RecyclerView, CardView et Palette

Rétro-compatibilité (2/2)

  • Personnaliser la palette de couleurs
  • Dépendances gradle pour le composant de rétro-compatibilité
<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
    <!-- customize the color palette -->
    <item name="colorPrimary">@color/material_blue_500</item>
    <item name="colorPrimaryDark">@color/material_blue_700</item>
    <item name="colorAccent">@color/material_green_A200</item>
</style>
dependencies {
    compile 'com.android.support:appcompat-v7:21.0.+'
    compile 'com.android.support:cardview-v7:21.0.+'
    compile 'com.android.support:recyclerview-v7:21.0.+'
}
  • Proposer une alternative aux fonctionnalités non-disponibles
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Call some material design APIs here
} else {
    // Implement this feature without material design
}

WebView

Présentation

  • Afficher des pages web comme la partie d'une interface graphique
  • N'inclue pas les fonctionnalités propres aux navigateurs (barre d'adresse, etc)
  • Vue privilégiée pour du contenu régulièrement modifié et accessible sur Internet
  • Utilisation recommandée si les données fournies à l'utilisateur nécessitent une connexion permanente
  • Plus simple que d'exécuter une requête, d'analyser le résultat et d'intégrer les données retournées
  • L'autorisation d'accès à Internet est nécessaire pour charger des pages
<!-- added in AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />

Utilisation

  • Déclarer le tag <WebView> dans un layout
<WebView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
myWebView.loadData("<html><body><h1>Hello world!</h1></body></html>",
    "text/html", "UTF-8");
  • Forcer l'ouverture des liens dans la WebView
myWebView.setWebViewClient(new WebViewClient());
  • Activer Javascript (désactivé par défaut)
myWebView.getSettings().setJavascriptEnabled(true);

Liaison Javascript

  • Possibilité d'interaction entre une page web et la WebView via Javascript
  • addJavascriptInterface() créer une interface utilisable en Javascript par les pages affichées dans la WebView
  • Pour des raisons de sécurité, n'utiliser que des pages conçues exclusivement pour l'application
  • Les messages de la console Javascript peuvent être récupérer dans l'application
public class WebViewActivity extends Activity {
    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (Uri.parse(url).getHost().equals("www.example.com")) { // This is my web site, so do not override,
                return false;                                         // let my WebView load the page
            }
            URLs Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); // Otherwise, the link is not 
            startActivity(intent);                                               // for a page on my site, so launch
            return true;                                                         // another Activity that handles 
        }
    }
    private class WebAppInterface {                                      // Exemple de code d’une page web
        Context mContext;                                                // utilisant l’interface associée :
        public WebAppInterface(Context c) {                              //   <script type="text/javascript">
            mContext = c;                                                //   function showAndroidToast(toast) {
        }                                                                //       Android.showToast(toast);
        @JavascriptInterface                                             //   }
        public void showToast(String toast) {                            //   </script>
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();  //   <input type="button"
        }                                                                //     onClick="showAndroidToast('Hello Android!')"
    }                                                                    //     value="Say hello" />
    private WebView myWebView;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.webpage);
        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.getSettings().setJavascriptEnabled(true);
        myWebView.setWebViewClient(new MyWebViewClient());
        myWebView.setWebChromeClient(new WebChromeClient() {
            public boolean onConsoleMessage(ConsoleMessage cm) {
                Log.d("MyApplication", cm.message() 
                    +" -- From line "+ cm.lineNumber() +" of "+ cm.sourceId());
                return true;
            }
        });
        myWebView.addJavascriptInterface (new WebAppInterface(this), "Android");
        myWebView.loadUrl("http://www.example.com");
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // Check if keyCode is the Back button and if there's history
        if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
            myWebView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event); // bubble up to the default system behavior
    }
}

Bonnes pratiques

  • Rediriger les utilisateurs d'appareils mobiles vers la version dédié d'un site Internet
    • Pour charger dans la WebView une version optimiser pour appareils mobiles
    • Le paramètre User-Agent de l'entête HTTP permet d'identifier si un appareil mobile est utilisé (s'il contient le mot "mobile")
  • Utiliser le doctype adapté aux appareils mobiles
    • xHML Basic 1.1 est le plus utilisé pour les applications mobiles
    • Penser à spécifier l'encodage (UTF-8 de préférence)
  • Redimensionner proprement les pages web
  • Minimiser les requêtes pour afficher les pages
  • Privilégier une navigation verticale
Made with Slides.com