ANDROID, PROLONGATION DE L’INTROdeptinfo.unice.fr/~renevier/ancien/POO-2013/cours...Université...
Transcript of ANDROID, PROLONGATION DE L’INTROdeptinfo.unice.fr/~renevier/ancien/POO-2013/cours...Université...
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 1 / 35 Université Nice Sophia Antipolis
ANDROID, PROLONGATION DE L’INTRO
Fragment,
Rotation,
Menu,
Activité,
Tests unitaires
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 2 / 35 Université Nice Sophia Antipolis
Retour sur les Fragments
Gestion de la rotation
Illustration avec le prochain sujet de TP
FRAGMENTS & ROTATION
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 3 / 35 Université Nice Sophia Antipolis
RETOUR SUR LES FRAGMENTS, PAR CODE
Ajouts
Retirer
Par FragmentTransaction (et commit)
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 4 / 35 Université Nice Sophia Antipolis
DÉPLACER LA VUE D’UN FRAGMENT
Il s’agit de déplacer la vue liée au fragment
Il faut donc le retrouver dans son parent, et le changer d’indice
Boucle for…
Car il manque :
private View[] getChilds(ViewGroup v) {
View [] res = null;
if (v != null) {
res = new View[v.getChildCount()];
for(int i = 0; i < res.length; i++) res[i] = v.getChildAt(i);
}
return res;
}
Pour changer l’indice, c’est la méthode <ViewGroup>.addView( v, i)… mais avant il faut l’enlever
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 5 / 35 Université Nice Sophia Antipolis
CYCLE DE VIE D’UN FRAGMENT
Source de l’image :
« android programming, the
big nerd ranch guide »
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 6 / 35 Université Nice Sophia Antipolis
ROTATION ET BUNDLE
Caution: ”Your activity will be destroyed and recreated each time the user
rotates the screen. When the screen changes orientation, the system destroys and
recreates the foreground activity because the screen configuration has changed and your
activity might need to load alternative resources (such as the layout).” http://developer.android.com/training/basics/activity-lifecycle/recreating.html
Re-créer => refaire des objets…
Pour les fragments, re-créer avec les mêmes paramètres initiaux
Sauvegarde de l’état avant destruction
onSaveInstanceState pour une Activité / onSaveInstanceState pour un fragment
on peut y remplir le bundle
Ne pas oublier l’appel à super.xxx
Méthode pour utiliser ce qui est sauvé
onCreate(Bundle saved) pour un fragment / onRestoreInstanceState(Bundle inState) pour
une activité
A faire évoluer… en même temps que le code…
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 7 / 35 Université Nice Sophia Antipolis
RETAINED FRAGMENT
Appel Méthode setRetainInstance(true) dans
onCreate du Fragment
android programming, the big nerd ranch guide
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 8 / 35 Université Nice Sophia Antipolis
FRAGMENT ET XML
L’attribut android:name attribute indique la classe qui extends Fragment
Insertion de la View retournée par onCreateView()
Note: chaque fragment nécessite un id (ou tag)
Meilleur gestion par Android
android:id attribute (unique)
android:tag avec une string unique
Les deux
Une View “fragment”
<fragment android:name=“nom_complet_classe" android:id="@+id/le_id" etc.
" />
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 9 / 35 Université Nice Sophia Antipolis
AUTRE FRAGMENT : DIALOGUE
Comme un fragment…
Un peu particulier (dialog)
Méthode show, pas de transaction
Besoin de prévoir la communication (interface
logicielle), notamment pour la valeur de retour
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 10 / 35 Université Nice Sophia Antipolis
MENU
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 11 / 35 Université Nice Sophia Antipolis
MENU ET XML
Dossier menu dans res <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/tri_buttoir" android:orderInCategory="100" android:showAsAction="never" android:title="@string/tri_buttoir"/> <item android:id="@+id/tri_creation" android:orderInCategory="100" android:showAsAction="never" android:title="@string/tri_creation"/> </menu>
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 12 / 35 Université Nice Sophia Antipolis
POSSIBILITÉ DU MENU EN XML
<menu> Defines a Menu, which is a container for menu items. A <menu> element must be the root node for the file and can hold one or more <item> and <group> elements.
<item> Creates a MenuItem, which represents a single item in a menu. This element may contain a nested<menu> element in order to create a submenu.
<group> An optional, invisible container for <item> elements. It allows you to categorize menu items so they share properties such as active state and visibility. For more information, see the section about Creating Menu Groups.
Pour la balise item :
android:id A resource ID that's unique to the item, which allows the application can recognize the item when the user selects it.
android:icon A reference to a drawable to use as the item's icon.
android:title A reference to a string to use as the item's title.
android:showAsAction Specifies when and how this item should appear as an action item in the action bar.
http://developer.android.com/guide/topics/ui/menus.html
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 13 / 35 Université Nice Sophia Antipolis
MENU ET CHARGEMENT DU XML
Méthode pour la création dans une Activity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.nom_du_fichier_xml, menu);
return true;
}
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 14 / 35 Université Nice Sophia Antipolis
ÉCOUTER LE MENU
Toujours dans une Activity @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.un_id: // des actions… return true; case R.id.un_autre_id: // des actions en réponse à cet item là return true; // et ainsi de suite default: return super.onOptionsItemSelected(item); } }
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 15 / 35 Université Nice Sophia Antipolis
EXEMPLE D’ACTION DE MENU
ANDROID ET JAVA
Le tri des tâches :
Comparable de
java
Enum de java
C.f. cours POO s5…
Puis gestion de la
rotation (re remplir la
liste de fragment, à
partir de leur
onCreate par un
appel à leur activité)
// pour alterner les tris
ComparerDate dernierTri = null;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.tri_buttoir:
if ((dernierTri != null) && (dernierTri.equals(ComparerDate.BUTTOIR_CROISSANT))) dernierTri = ComparerDate.BUTTOIR_DECROISSANT;
else dernierTri = ComparerDate.BUTTOIR_CROISSANT;
break;
case R.id.tri_creation:
if ((dernierTri != null) && (dernierTri.equals(ComparerDate.CREATION_CROISSANT))) dernierTri = ComparerDate.CREATION_DECROISSANT;
else dernierTri = ComparerDate.CREATION_CROISSANT;
break;
default:
return super.onOptionsItemSelected(item);
}
trier(dernierTri);
return true; }
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 16 / 35 Université Nice Sophia Antipolis
ACTIVITÉ(S)
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 17 / 35 Université Nice Sophia Antipolis
LANCER UNE ACTIVITÉ DEPUIS UNE AUTRE
Méthode simple : startActivity(Intent i) dans Activity
Intent
Objet de communication avec l’OS
Dans ce cas, constructeur new Intent(Context, Class)
Context = activité qui lance
Class = le type de l’activité à lancer
putExtra pour ajouter des données
Dans le onCreate de la nouvelle activité
getIntent( )
getIntent().getIntExtra(“clef", val_defaut);
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 18 / 35 Université Nice Sophia Antipolis
RETOUR DE DONNÉES
Méthode setResult(code, intent) Avec un code de retour
Avec un Intent (et des extra)
Méthode dans l’activité qui a lancé
protected void onActivityResult(int requestcode, int resultcode, Intent data) { if (data == null) return ; Date d = (Date) data.getSerializableExtra("date"); ((TextView) findViewById(R.id.textView1)).setText(formatDate.format(d)); }
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 19 / 35 Université Nice Sophia Antipolis
CYCLE DE MESSAGE
…
android programming, the big nerd ranch guide
APPLI
Extra, etc.
Activité A
Activité B
Extra, etc.
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 20 / 35 Université Nice Sophia Antipolis
TUER UNE ACTIVITÉ ? VIDER LA PILE ?
Pour « tuer » « Normalement réserver à l’OS »
finish() (+System.exit(0) )
Etc.
Pour relancer l’application au début à partir de là ou on est : Dans le manifest, pour L’activité ciblée
android:launchMode="singleTop“
Depuis l’endroit où on veut revenir « au début »
Intent intent = new Intent(this, LActivitéPrincipale.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 21 / 35 Université Nice Sophia Antipolis
TESTS ET ANDROID
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 22 / 35 Université Nice Sophia Antipolis
PRINCIPES
Un framework de test intégré dans le sdk
JUNIT 3 pour l’extension pour android
Junit « normal » pour le java « normal »
Suites de tests dans des environnements semblables aux applications
Intégration dans l’IDE
Doc : https://developer.android.com/tools/testing/index.html
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 23 / 35 Université Nice Sophia Antipolis 23
EN JUNIT 3, LES TESTCASES…
Ecrire des sous-classes de TestCase
Un TestCase peut définir un nombre quelconque de méthodes testXxx()
Pour vérifier les résultats attendus (écrire des oracles !), il faut appeler
une des nombreuses variantes de méthodes assertYYY () fournies
assertTrue(String message, boolean test), assertFalse(…)
assertEquals(…) : test d’égalité avec equals
assertSame(…), assertNotSame(…) : tests d’égalité de référence
assertNull(…), assertNotNull(…)
Fail(…) : pour lever directement une AssertionFailedError
Surcharge sur certaines méthodes pour les différentes types de base
d’après un cours de Philippe Collet
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 24 / 35 Université Nice Sophia Antipolis 24
JUNIT 3 : DIAGRAMME DE CLASSES
MonTest
Une méthode testXXX() par test
d’après un cours de Philippe Collet
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 25 / 35 Université Nice Sophia Antipolis 25
JUNIT 3.X JUNIT 4 package junit4;
import calc.Calculator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
private static Calculator calculator =
new Calculator();
@Before
public void clearCalculator() {
calculator.clear();
}
@Test
public void add() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(), 2);
}
package junit3;
import calc.Calculator;
import junit.framework.TestCase;
public class CalculatorTest extends
TestCase {
private static Calculator calculator =
new Calculator();
protected void setUp() {
calculator.clear();
}
public void testAdd() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(),
2);
}
d’après un cours de Philippe Collet
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 26 / 35 Université Nice Sophia Antipolis 26
JUNIT 3.X JUNIT 4
@Test(expected =
ArithmeticException.class)
public void divideByZero() {
calculator.divide(0);
}
@Ignore("not ready yet")
@Test
public void multiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),
100);
}
}
public void testDivideByZero() {
try {
calculator.divide(0);
fail();
} catch (ArithmeticException e) {
}
}
public void notReadyYetTestMultiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),
100);
}
}
d’après un cours de Philippe Collet
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 27 / 35 Université Nice Sophia Antipolis
INTÉGRATION DANS ECLIPSE (1/2)
Utilisation facile dans eclipse (sinon ligne de commande)
File > New > Project…
Ouvre la boite de dialogue Select a Wizard
Choisir dans Android, Android Test Project,
Choix du nom (xxxTest)
Choix de l’emplacement
Dans un projet à part entière
dans un projet à part entière placé dans
un dossier test d’un projet;…
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 28 / 35 Université Nice Sophia Antipolis
INTÉGRATION DANS ECLIPSE (1/2)
Choix du projet testé
On peut le changer dans le
manifest et dans la configuration
du build path
Choix du sdk cible
Cohérence avec choix du projet
Pré-rempli
Par convention (optionnel)
Renommage du dossier src
Renommage du package de test
(package de l’appli + .test)
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 29 / 35 Université Nice Sophia Antipolis
TESTCASE ANDROID
Test d’activité ActivityInstrumentationTestCase2
Pour tester les activités dans un “contexte” habituel possibilité pour de mock objects pour les Intents (communications entre application)
ActivityUnitTestCase Isolation (hors contexte) Mock Object pour contexte et d’application
SingleLaunchActivityTestCase invokes setUp() and tearDown() only once no mock objects.
Et d’autres… (service, content provider (save & restore date, UI Monkey test)
ViewAsserts pour des tests sur la positions des View
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 30 / 35 Université Nice Sophia Antipolis
LE TEST PAR L’EXEMPLE, TOUJOURS AVEC LA
TODO LIST
Un exemple dans le sdk… Spinner(Activity)Test
ActivityInstrumentationTestCase2
Tests et le UI Thread
Certaines actions interdites en dehors du thread graphique
Méthode runOnUiThread, synchronisation getInstrumentation().waitForIdleSync();
Attention aux blocages ou à la gestion de la file d’attente des événéments
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 31 / 35 Université Nice Sophia Antipolis
EXEMPLE DE TEST D’INTERFACE : L’AJOUT
(NOMBRE) DE FRAGMENT public void testAjout() {
// focus sur le edittext, normalement par defaut... mais runOnUI( new Runnable() { public void run() { nouveau.requestFocus(); } } ); for (int i = 0; i < taches.length; i++) { getInstrumentation().sendStringSync(taches[i]); // ajouter.callOnClick(); // provoque android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. runOnUI(clickOnView(ajouter)); assertEquals("nombre de vue sous liste", i+1, liste.getChildCount()) }
}
Cache runOnUiThread
et waitForIdleSync
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 32 / 35 Université Nice Sophia Antipolis
EXEMPLE : LE DEPLACEMENT…
public void testChangement() {
testAjout();
// on recupere les Items
View [] items = getChilds(liste);
int nb_items = items.length;
// on memorise le premier apres les ajouts... qui sera deplace en dernier en n-1 etapes
View first = items[0];
ImageButton down = (ImageButton) first.findViewWithTag("down");
for(int i = 0; i < nb_items-1; i++) {
assertEquals("nombre de vue sous liste", taches.length, items.length);
// on deplace le 1er au dernier
runOnUI(clickOnView(down));
// on recupere a nouveau les Item
items = getChilds(liste);
assertEquals("l'item est en "+(i+1), items[i+1], first);
}
}
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 33 / 35 Université Nice Sophia Antipolis
GESTION DE LA VIE DES ACTIVITÉS : ET LA
ROTATION ?
Rotation => création à nouveau…
Donc… nouvelle activité, nouveaux objets…
Les objets initiaux (setUp) sont périmés…
Pour récupérer la nouvelle instance… il faut
passer par Robotium (plus simple)
https://code.google.com/p/robotium/
S’ajoute au projet
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 34 / 35 Université Nice Sophia Antipolis
POUR LA ROTATION
Changement d’orientation avec Robotium
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) mSolo.setActivityOrientation(Solo.LANDSCAPE); else mSolo.setActivityOrientation(Solo.PORTRAIT); Synchro sur la création de l’activité et qu’elle ne fasse plus
rien
mSolo.waitForActivity(TodoList.class); getInstrumentation().waitForIdleSync(); On peut récupérer les nouveaux objets...
todo = (TodoList) mSolo.getCurrentActivity(); //this will return new activity liste = (LinearLayout) todo.findViewById(id.liste);
L3I– POO - Semestre 6 –Android, Rotation, Menu, Activité, Test– Philippe Renevier Gonin 35 / 35 Université Nice Sophia Antipolis
EXEMPLES DE PIEGE…
Gestion des threads
Rotation
Utilisation d’objet ou propriété non initialisés
Par exemple… un tag