tite fractale

Android activities

1. Introduction

Du copier-coller, oui. Mais du copier-coller idiot, non.

Le code fourni nécessite de votre part une compréhension. Il représente votre travail de programmeur, de fouille sur Internet, avec compréhension, tests et modifications appropriées.

Ce code vous est fourni afin que cette activité ne soit pas trop lourde.

Un conseil : consultez régulièrement developer.android.com (in English of course).

2. Structure globale

2.1. Création d’un projet

Créer votre premier projet Android appelé Projet_1_VOTRENOM.

Pour Minimum Required SDK, prendre API 14: Android 4.0 pour éviter la problématique des appcompat (compatibilité avec la version 7 et la version 11 lors des changement de machines et des signatures de l'application appcompat_v7).

new android app

Puis créer une activité vierge :

blank activity

pour au final avoir cette arborescence :

arbo

Si cette arborescence n’apparaît pas avec Eclipse, cliquer en haut à gauche sur l’icône « carré sur carré ». Il est conseillé de « sauver une perspective » en début de session pour pouvoir la restaurer si des sous-fenêtres se mettent à disparaître.

2.2. Commentaires sur l’arborescence

2.3. Ressources et dimensions

Les tailles et résolutions des appareils ciblés sont tellement différentes qu’il a fallu utiliser une taille plus ou moins indépendante des résolutions. le dp. Cela correspond aux pixels d’un écran de 160 dpi.

Pour plus de détails, voir cette page et cette BD.

Concernant les différentes ressources à fournir, voir cette page.

3. Pour tester

Avec Eclipse, il faut établir une « Run configuration » avant de pouvoir cliquer sur « Run ». Ensuite, votre IDE vous demandera quelle cible exécutera votre application.

Ici, plusieurs possibilités :

3.1. Sur un vrai appareil

Il faut que l’appareil :

3.2. AVD

Ce sont les émulateurs fournis par le SDK. C’est là que les Systeme Images entrent en jeu.

3.3. Genymotion

4. Première activité

4.1. Qu’est-ce qu’une activité

En observant l'architecture de la majorité des applications Android, vous remarquerez une construction toujours à peu près similaire. Une application est un assemblage de fenêtres entre lesquelles il est possible de naviguer.

Ces différentes fenêtres sont appelées des activités. Un moyen efficace de différencier des activités est de comparer leur interface graphique : si elles sont radicalement différentes, c'est qu'il s'agit d'activités différentes. De plus, comme une activité remplit tout l'écran, votre application ne peut en afficher qu'une à la fois.

Une activité est un support sur lequel nous allons greffer une interface graphique et dans laquelle nous pourront coder les comportements de ces éléments graphiques. Cependant, ce n'est pas le rôle de l'activité que de créer et de disposer les éléments graphiques, elle n'est que l’échafaudage sur lequel vont s'insérer les objets graphiques. C’est le système Android qui instancie et place les éléments suivant les directives des fichiers dans layout*. Pire encore, une activité n’est pas maîtresse de son destin (voir plus loin).

Néanmoins, une activité contient des informations sur l'état actuel de l'application : ces informations s'appellent le context. Ce context constitue un lien avec le système Android ainsi que les autres activités de l'application.

L’activité vierge créée contient par défaut un layout et un TextView.

4.2. Les layouts

Une interface graphique pour Android est constituée uniquement de vues (éléments dont le nom se termine par View, comme TextView, ImageView, WebView… Ainsi, tous les nœuds des fichiers XML de layout* seront des vues.

Les layouts englobent d'autres objets graphiques pour les mettre en place. Un layout est donc une vue spéciale qui peut contenir d'autres vues et qui n'est pas destinée à fournir du contenu ou des contrôles à l'utilisateur. Les layouts se contentent de disposer les vues d'une certaine façon. Les vues contenues sont les enfants, la vue englobante est le parent, comme en XML. Une vue qui ne peut pas en englober d'autres est appelée un widget (en français, un composant). Ne pas confondre avec les widgets que l’on installe sur l’écran d’accueil.

Comme beaucoup de nœuds en XML, une vue peut avoir des attributs, qui permettent de moduler certains de ses aspects. Certains de ces attributs sont spécifiques à des vues, d'autres sont communs. Parmi ces derniers, les deux plus courants sont :

Ces deux attributs peuvent prendre une valeur parmi les trois suivantes :

common attributes

Il est très conseillé de remplacer tout de suite le RelativeLayout par un layout vertical. Mais pourquoi vertical et pas horizontal ?

4.3. Quelques éléments ou vues importants

En plus des fondamentaux (TextView, EditView, Button et ImageView), voici quelques éléments importants :

Vous pouvez essayer d’ajouter une image à cette première activité, en prenant pour source l’icône de démarrage.

4.4. Multi-langue

Pour mettre en place le multi-langue dans votre application Android il faut simplement créer un strings.xml dans le bon sous-répertoire du répertoire res. Il faut nommer ce sous-répertoire : value-{codification du pays}.

arbo montrant values-fr/strings.xml

Faire le test avec une traduction anglaise/française de @string/hello_world et en modifiant la langue sur les paramètres de l’appareil.

5. D’une activité à une autre

5.1. Vers l’activité 1

5.1.1. Depuis un bouton

Ajouter quatre boutons à votre layout activity_main.xml comme décrit ci dessous.

act1_layout_struct act1_layout_viz

Veillez à respecter les identifiants act1, act2

Dans le contexte d'une interface graphique, les listeners permettent au programmeur de faire réagir l’appareil aux actions de l'utilisateur (clic de souris, enfoncement de touche du clavier, etc). Les listeners sont des interfaces. Ces interfaces fournissent une ou plusieurs méthodes qui peuvent donc être implémentées différemment selon les cas et les besoins, pour répondre aux événements.

Ajouter ces lignes à la méthode onCreate de votre MainActivity :

Button act1=(Button) findViewById(R.id.act1);  // act1 doit correspondre à l’identifiant
Button act2=(Button) findViewById(R.id.act2);  // du bouton créé précédemment.
Button act3=(Button) findViewById(R.id.act3);
Button act4=(Button) findViewById(R.id.act4);
act1.setOnClickListener(btnclick);
act2.setOnClickListener(btnclick);
act3.setOnClickListener(btnclick);
act4.setOnClickListener(btnclick);

Puis créez la méthode btnclick, désignée dans les quatre lignes précédentes :

private OnClickListener btnclick = new OnClickListener() {
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.act1:
                Toast.makeText(getApplicationContext(), "clic sur act1", Toast.LENGTH_LONG).show();
                break;
            case R.id.act2:
                Toast.makeText(getApplicationContext(), "clic sur act2", Toast.LENGTH_LONG).show();
                break;
            case R.id.act3:
                Toast.makeText(getApplicationContext(), "clic sur act3", Toast.LENGTH_LONG).show();
                break;
            case R.id.act4:
                Toast.makeText(getApplicationContext(), "clic sur act4", Toast.LENGTH_LONG).show();
                break;
        }
    }
};

Cet OnClickListener est celui de android.view.View.

Tester.

5.1.2. Depuis un menu

Dans res/menu/main.xml, ajouter les items :

<item
    android:id="@+id/menu_act1"
    android:showAsAction="ifRoom|withText"
    android:title="@string/menu_act1"/>
<item
    android:id="@+id/menu_act2"
    android:showAsAction="ifRoom|withText"
    android:title="@string/menu_act2"/>
<item
    android:id="@+id/menu_act3"
    android:showAsAction="ifRoom|withText"
    android:title="@string/menu_act3"/>
<item
    android:id="@+id/menu_act4"
    android:showAsAction="ifRoom|withText"
    android:title="@string/menu_act4"/>

Voir cette page pour plus d’infos sur les menus.

Dans res/values/strings.xml, ajouter les valeurs :

<string name="menu_act1">Acti1</string>
<string name="menu_act2">Acti2</string>
<string name="menu_act3">Acti3</string>
<string name="menu_act4">Acti4</string>

Dans MainActivity.java, remplacer la méthode onOptionsItemSelected(MenuItem item) par :

    switch (item.getItemId()) {
        case R.id.menu_act1:    
            Toast.makeText(getApplicationContext(), "clic sur act1", Toast.LENGTH_LONG).show();
            return true;
        case R.id.menu_act2:    
            Toast.makeText(getApplicationContext(), "clic sur act2", Toast.LENGTH_LONG).show();
            return true;
        case R.id.menu_act3:        
            Toast.makeText(getApplicationContext(), "clic sur act3", Toast.LENGTH_LONG).show();
            return true;
        case R.id.menu_act4:        
            Toast.makeText(getApplicationContext(), "clic sur act4", Toast.LENGTH_LONG).show();
            return true;
        case R.id.action_settings:
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }

Tester.

5.1.3. Enfin une autre activité

Ajouter une nouvelle activité Act1 (clic droit sur le package principal du src, choisir new).

new_android_act

Insérer un bouton « fin » sur cette activité qui, si on clique dessus, appelle la méthode finish() (héritée de Activity).

Dans le monde Android, un intent doit se comprendre comme un message que la plate-forme Android envoie quand elle veut demander une action de la part d'une autre application, comme le lancement d'une autre activité (cette activité pouvant faire partie de la même appli, c’est ce que nous allons mettre en place).

Ajouter au clic du bouton act1 :

Intent i = new Intent(getApplicationContext(), Act1.class);
startActivity(i);

Tester votre application avec un clic sur act1.

Maintenant que vous savez démarrer une activité, vous pouvez remplacer les toasts par des démarrages d’activité.

5.2. Vers l’activité 2

Créer une activité 2 à l'identique de l’activité 1. Nous allons lui passer des données.

La classe Bundle est généralement utilisé pour transmettre des données entre différentes activités. Cette « valise » contient autant de données que désiré.

Ajouter au clic du bouton act2 :

Intent i = new Intent(getApplicationContext(), Act2.class);
i.putExtra("param1", "Démarrage activité : ");
i.putExtra("param2", 2);
startActivity(i);

Ajouter à la méthode onCreate de l'activity 2 :

Bundle b = getIntent().getExtras();
String param1 = b.getString("param1"); 
int param2 = b.getInt("param2");
Toast.makeText(getApplicationContext(), param1 + Integer.toString(param2), Toast.LENGTH_LONG).show();

Tester en vérifiant bien que les valeurs envoyées depuis l’activité principale arrivent à l’activité 2.

5.3. Vers l’activité 3

Dans l'activité principale, ajouter ces codes. Ils permettent de connaître l'activité appelée qui nous retourne une valeur.

static final int CODE_RETOUR_ACTIVITY3 = 3;
static final int CODE_RETOUR_ACTIVITY4 = 4;

Ajouter dans le case R.id.act3 de la MainActivity :

Intent i = new Intent(getApplicationContext(), Act3.class);
startActivityForResult(i, CODE_RETOUR_ACTIVITY3);

Ajouter à l’activité principale la méthode :

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // on regarde quelle Activity a répondu
    switch (requestCode) {
        case CODE_RETOUR_ACTIVITY3:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    Toast.makeText(getApplicationContext(), "retour act3 ok", Toast.LENGTH_LONG).show();
                    return;

                case Activity.RESULT_CANCELED:
                    Toast.makeText(getApplicationContext(), "retour act3 cancel", Toast.LENGTH_LONG).show();
                    return;
            }
        case CODE_RETOUR_ACTIVITY4:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    Bundle b = data.getExtras(); 
                    String string_1 = b.getString("string_1", "");
                    String string_2 = b.getString ("string_2", "");
                    int int_1 = b.getInt("int_1", 0);
                    long long_1 = b.getLong ("long_1", 0);
                    String msg = "retour act4 ok : " + string_1 + " " + string_2 + " " + Integer.toString(int_1) + " " + Long.toString(long_1);
                    Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
                    return;
                case Activity.RESULT_CANCELED:
                    Toast.makeText(getApplicationContext(), "retour act4 cancel", Toast.LENGTH_LONG).show();
                    return;
        }
    }
}

Créer une activité 3 avec deux boutons (« ok » et « cancel »). Réagir au clic sur « ok » par le bon code de retour et un finish() :

Button finok=(Button) findViewById(R.id.btnokact3);
finok.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        setResult(Activity.RESULT_OK);
        finish();
    }
});

Dans l'activité 3, au clic sur « cancel », on retourne le code correspondant à « cancel » : RESULT_CANCEL.

Tester.

5.4. Vers l’activité 4

Créer une activité 4 à l’identique de l’activité 3, mais en ajoutant des EditText comme ci-dessous.

act4_layout_struct act4_layout_viz

Dans l'activité 4 le clic de ok devra renvoyer le contenu du formulaire :

Intent intent = new Intent ();
intent.putExtra ("string_1", ((EditText)findViewById(R.id.nom)).getText().toString());
intent.putExtra ("string_2", ((EditText)findViewById(R.id.prenom)).getText().toString());
intent.putExtra ("int_1", Integer.parseInt(((EditText)findViewById(R.id.age)).getText().toString()));
intent.putExtra ("long_1", Long.parseLong(((EditText)findViewById(R.id.numfetiche)).getText().toString()));
setResult (Activity.RESULT_OK, intent);
finish();

Ajouter dans la MainActivity sur case R R.id.act4 :

Intent i = new Intent(getApplicationContext(), Act4.class);
startActivityForResult(i, CODE_RETOUR_ACTIVITY4);

Tester.

5.5. Vers et depuis une activité inconnue

Dans cet article Wikipedia, vous trouverez cette phrase :

Write programs that do one thing and do it well.

Un exemple parlant : ne mettez pas en place une fonctionnalité d’envoi d’email dans votre application, confiez l’envoi d’email à une application qui le fait bien. L’utilisateur pourrait même choisir l’application d’envoi d’email qu’il préfère.

5.5.1. Récupération de texte

Créer une activité 5 contenant uniquement un TextView. Pas besoin de bouton dans l’activité principale (une activité inconnue la démarrera). Vous déclarerez dans AndroidManifest.xml que cette activité sait récupérer du texte, puis coderez dans son onCreate l’affichage du texte dans la TextView.

Inspirez-vous de ce commit, mais lancez un Toast au lieu de remplir le contenu de la webview (dont vous n’avez donc pas besoin).

Lancez un navigateur dans votre appareil Android, et naviguez vers une page contenant au moins un paragraphe. Un clic long vous permettra de sélectionner une partie du texte, que vous pourrez partager avec un bouton qui sera apparu en haut à droite. Votre activité 5 devrait apparaître pour récupérer ce fragment de texte partagé.

Une fois que cela fonctionne, changez le texte avant de l’afficher dans la TextView, en remplaçant toutes les voyelles par des « u ». Vous pouvez par exemple utiliser String.replace.
Ça peut être drôle.

Tester.

5.5.2. Envoi de texte

Ajouter un bouton « Partager » a l’activité 5. Le code à exécuter au clic sur ce bouton pourrait ressembler à :

Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
String shareTitle = "Le titre";
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, shareTitle);
String shareBody = "Le texte à partager";
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
startActivity(Intent.createChooser(sharingIntent, "Partager avec"));

Tester.

6. États d'une activité

6.1. Cycle de vie

Si un utilisateur reçoit un appel, il devient plus important qu'il puisse y répondre que d'émettre la chanson que votre application diffuse. Pour pouvoir toujours répondre à ce besoin, les développeurs d'Android ont eu recours à un système particulier :
À tout moment votre application peut laisser place à une autre application, qui a une priorité plus élevée. Si votre application utilise trop de ressources système, alors elle empêchera le système de fonctionner correctement et Android l'arrêtera sans vergogne.

Votre activité existera dans plusieurs états au cours de sa vie (active/en pause/stoppée), par exemple un état actif pendant lequel l'utilisateur l'exploite, et un état de pause quand l'utilisateur reçoit un appel.

Pour être plus précis, quand une activité A se lance, elle se met tout en haut de ce qu'on appelle la pile d'activités. Quand une autre activité B prend la main, elle s’empile, puis quand celle-ci se ferme, elle fera apparaître l’activité A juste en dessous.

Voici le cycle de vie d’une activité :

cycle de vie d’une activité (flowchart)

6.2. En pratique

6.2.1. La jungle

Créer un nouveau projet Projet2_VOTRENOM et ajouter dans l’activité principale les composants suivants :

act_bascule

Les composants sont deux boutons Sauve et Affiche, et un EditText ayant pour « hint » (suggestion) « Test saisie ».

Ajouter à la méthode onCreate de votre MainActivity :

Log.w("P2", "onCreate");
Button act1 = (Button) findViewById(R.id.button1);
Button act2 = (Button) findViewById(R.id.button2);
act1.setOnClickListener(btnclick);
act2.setOnClickListener(btnclick);

et la méthode associée btnclick :

private OnClickListener btnclick = new OnClickListener() {
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                vsave = ((EditText)findViewById(R.id.editText1)).getText().toString();
                break;
            case R.id.button2:
                Toast.makeText(getApplicationContext(), vsave, Toast.LENGTH_LONG).show();
                break;
    }
}

Attention, ne pas déclarer et initialiser vsave dans btnclick. Plutôt la déclarer comme membre de votre activité.

Puis on ajoute les méthodes-espionnes suivantes :

@Override
public void onStart() { super.onStart();Log.w("P2", "onStart"); }
@Override
public void onRestart() { super.onRestart();Log.w("P2", "onRestart"); }
@Override
public void onResume() { super.onResume();Log.w("P2", "onResume"); }
@Override
public void onPause() { super.onPause();Log.w("P2", "onPause"); }
@Override
public void onStop() { super.onStop();Log.w("P2", "onStop"); } 
@Override
public void onDestroy() { super.onDestroy();Log.w("P2", "onDestroy "); }

Lancer votre application.

Ajouter dans le logcat un filtre pour ne voir que le tag P2 (comme projet 2) et analyser le logcat à partir de votre filtre.

Tester votre application :

6.2.2. Gestion de la rotation

Pour indiquer à Android que l'activité peut gérer la rotation de l'écran, il suffit de l'indiquer dans AndroidManifest.xml. Dans la section de l'activité, ajouter la ligne avec android:configChanges.

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Implémenter cette gestion de la rotation et tester à nouveau la rotation.

Afin de simuler un destroy de l'application par le système, on peut supprimer cette ligne et basculer l’écran.

6.2.3. Utiliser InstanceState

Android propose des méthodes de sauvegarde et de restauration automatiques :

save_restore.png

Ajouter les méthodes de sauvegarde et de restauration automatique :

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    Log.w("P2", "onSaveInstanceState ");
    super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    Log.w("P2", "onRestoreInstanceState ");
}

Tester à nouveau la rotation pour simuler un destroy par le système.
Analyser le logcat.

Modifier ces deux méthodes afin de sauvegarder et de restaurer l’EditText et notre variable save.

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    Log.w("P2", "onSaveInstanceState");
    savedInstanceState.putString("save", vsave);
    savedInstanceState.putString("editText", ((EditText) findViewById(R.id.editText1)).getText().toString());
    super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    vsave = savedInstanceState.getString("save");
    ((EditText) findViewById(R.id.editText1)).setText(savedInstanceState.getString("editText"));
    Log.w("P2", "onRestoreInstanceState");
}

Tester à nouveau la rotation pour simuler un destroy par le système.

6.3. Simuler des variables globales

Nous allons ici utiliser la classe SharedPreferences. Android vous permet de sauvegarder et de récupérer des paires clé-valeur de type de données primitifs. Ces préférences sont sauvegardées même sur une pause ou un stop de l'activity (voir plus loin).

Il est possible d’utiliser JSON pour des données complexes.

JSON (JavaScript Object Notation – Notation Objet issue de JavaScript) est un format léger d'échange de données. Il est facile à lire ou à écrire pour des humains. Il peut être aisément analysé ou généré par les machines. JSON est maintenant un format texte complètement indépendant de tout langage, qui remplace peu à peu XML.

JSON se base sur deux structures : Une collection de couples nom/valeur. Divers langages la réifient par un  objet, un enregistrement, une structure, un dictionnaire, une table de hachage, une liste typée ou un tableau associatif. Une liste de valeurs ordonnées. La plupart des langages la réifient par un  tableau, un vecteur, une liste ou une suite.

Voir ces exemples.

Pour écrire, on utilise SharedPreferences.Editor :

Pour lire, on utilise SharedPreferences :

Dans la méthode onCreate de l’activité principale, ajouter :

SharedPreferences myPrefs = this.getSharedPreferences("mesvariablesglobales", 0);
SharedPreferences.Editor prefsEditor = myPrefs.edit();
prefsEditor.putString("svar1", "Bonjour");
prefsEditor.putInt("ivar2", 2);
prefsEditor.commit();

Dans la méthode onCreate de l'activité 1 ajouter :

SharedPreferences myPrefs = this.getSharedPreferences("mesvariablesglobales", 0);
String svar1 = myPrefs.getString("svar1", "nothing");
int ivar2 = myPrefs.getInt("ivar2", 0);
Toast.makeText(getApplicationContext(), svar1 + " " + Integer.toString(ivar2), Toast.LENGTH_LONG).show();

Tester.

Visionner votre fichier mesvariablesglobales.xml via windows / show view / android / file explorer. Puis se positionner dans data / data / votre application / shared_prefs (en haut à droite pull file from a device).




Adapté d’une formation avec PFR, le 25/02/2017, 16h49'24".






Page générée le 27/05/2021, 09h53'27" (source).
historique de la page
historique global