• Aucun résultat trouvé

On duplique le projet [Exemple-10] dans [Exemple-11] :

1.12.2 Objectifs

La nouvelle application aura deux onglets :

• le 1er onglet affichera toujours le fragment [Vue1] ;

• en [1], le fragment [Vue1] ;

• en [2], le fragment de type [PlaceholderFragment] choisi par l'utilisateur ;

• en [3], on continue à compter les visites ;

1.12.3 La session

La nouvelle session sera la suivante : 1. package exemples.android; 2.

3. import org.androidannotations.annotations.EBean; 4.

5. @EBean(scope = EBean.Scope.Singleton) 6. publicclass Session {

7. // nombre de fragments visités

8. private int numVisit;

9. // n° fragment de type [PlaceholderFragment] affiché dans second onglet

10. private int numFragment; 11.

13. ... 14. }

• ligne 10 : nous allons gérer nous-mêmes le clic sur les onglets. Lorsqu'on clique sur un onglet, il faut restituer le fragment

qu'il affichait la dernière fois qu'il était sélectionné. Le champ [numFragment] mémorisera le n° de celui-ci pour l'onglet n° 2, un nombre dans [0, Fragments_COUNT-2]. Lorsque l'onglet n° 2 sera cliqué, on ira chercher dans la session, le n° du fragment à afficher ;

1.12.4 Le menu

Le menu [res / menu / menu_main.xml] évolue de la façon suivante : 1. <menu xmlns:android="http://schemas.android.com/apk/res/android"

2. xmlns:app="http://schemas.android.com/apk/res-auto"

3. xmlns:tools="http://schemas.android.com/tools"

4. tools:context="exemples.android.MainActivity"> 5. <item android:id="@+id/action_settings"

6. android:title="@string/action_settings"

7. android:orderInCategory="100"

8. app:showAsAction="never"/> 9. <item android:id="@+id/fragment1"

10. android:title="@string/fragment1"

11. android:orderInCategory="100"

12. app:showAsAction="never"/> 13. <item android:id="@+id/fragment2"

14. android:title="@string/fragment2"

15. android:orderInCategory="100"

16. app:showAsAction="never"/> 17. <item android:id="@+id/fragment3"

18. android:title="@string/fragment3"

19. android:orderInCategory="100"

20. app:showAsAction="never"/> 21. <item android:id="@+id/fragment4"

22. android:title="@string/fragment4"

23. android:orderInCategory="100"

24. app:showAsAction="never"/> 25. </menu>

L'onglet n° 2 affichera l'un des quatre fragments des lignes 9-24. Le 5ième fragment est le fragment [Vue1Fragment] qui sera lui toujours affiché dans l'onglet n° 1.

1.12.5 La classe [MainActivity]

La classe [MainActivity] doit désormais gérer les onglets et la navigation entre eux, ce qu'elle ne faisait pas jusqu'à maintenant. Son code évolue de la façon suivante :

1. // le gestionnaire d'onglets

2. @ViewById(R.id.tabs)

3. protected TabLayout tabLayout; 4. ...

5. @AfterViews

6. protected void afterViews() { 7. // log

8. if (IS_DEBUG_ENABLED) {

9. Log.d("MainActivity", "afterViews"); 10. } 11. ... 12. 13. // pas de scrolling 14. mViewPager.setScrollingEnabled(false); 15.

19. // au départ on n'a qu'un seul onglet

20. TabLayout.Tab tab = tabLayout.newTab(); 21. tab.setText("Vue 1");

22. tabLayout.addTab(tab); 23.

24. // gestionnaire d'évt

25. tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { 26. @Override

27. public void onTabSelected(TabLayout.Tab tab) {

28. // un onglet a été sélectionné - on change le fragment affiché par le conteneur de fragments

29. ... 30. } 31.

32. @Override

33. public void onTabUnselected(TabLayout.Tab tab) { 34.

35. } 36.

37. @Override

38. public void onTabReselected(TabLayout.Tab tab) { 39. 40. } 41. }); 42. 43. ... 44. 45. }

• ligne 17 : le 1er fragment affiché par le conteneur de fragments sera le fragment [Vue1Fragment]. Par construction, ce sera

le dernier fragment du conteneur ;

• lignes 20-22 : parce qu'on n'a pas fait d'association entre onglets et conteneur de fragments, nous devons gérer les onglets

nous-mêmes. Au départ, la barre d'onglets [tabLayout] de la ligne 3 n'a aucun onglet ;

• ligne 20 : on crée le 1er onglet ;

• ligne 21 : on lui donne un titre. Dans les exemples précédents, le titre des onglets était le titre des fragments. C'est

désormais fini. Du coup, on retire la méthode [getPageTitle] du gestionnaire de fragments. On n'en a plus besoin : 1. // facultatif - donne un titre aux fragments gérés

2. @Override

3. public CharSequence getPageTitle(int position) { 4. return String.format("Onglet n° %s", (position + 1)); 5. }

• ligne 22 : l'onglet créé est ajouté à la barre d'onglets. Notre barre d'onglets a désormais un onglet. Qu'affiche cet onglet ? Il faut comprendre que les onglets et les fragments sont deux notions indépendantes. Le fragment affiché est toujours celui choisi par le conteneur de fragments. Si on change d'onglet et qu'on ne demande pas au conteneur de changer le fragment affiché, rien ne se passe : c'est toujours le même fragment qui est affiché mais l'onglet sélectionné a lui changé. Donc ici, le fragment affiché est celui choisi ligne 17 : le fragment [Vue1Fragment] ;

• lignes 26-30 : la méthode à écrire pour gérer le changement d'onglet par l'utilisateur ;

La méthode [onTabSelected] des lignes 26-30 est déclenchée dès qu'il y a changement d'onglet (si l'utilisateur clique sur un onglet déjà sélectionné, il ne se passe rien). Son code est le suivant :

1. @Override

2. public void onTabSelected(TabLayout.Tab tab) { 3. if (IS_DEBUG_ENABLED) {

4. Log.d("onglets", "onTabSelected"); 5. }

6. // un onglet a été sélectionné - on change le fragment affiché par le conteneur de fragments

7. // position de l'onglet

8. int position = tab.getPosition(); 9. // n° du fragment à afficher 10. int numFragment; 11. switch (position) { 12. case 0: 13. // n° fragment [Vue1Fragment] 14. numFragment = FRAGMENTS_COUNT - 1; 15. break; 16. default: 17. // n° fragment [PlaceholderFragment] 18. numFragment = session.getNumFragment(); 19. } 20. // affichage fragment 21. mViewPager.setCurrentItem(numFragment); 22. }

• ligne 8 : on récupère la position de l'onglet qui a été cliqué. On va récupérer ici un nombre 0 ou 1 ;

• lignes 12-15 : si c'est le premier onglet qui a été cliqué, on se prépare à afficher le fragment [Vue1Fragment] ;

• lignes 16-18 : dans les autres cas (onglet n° 2 cliqué), on se prépare à réafficher le fragment qui était affiché la dernière fois que l'onglet n° 2 était sélectionné. Le n° de celui-ci avait alors été mis dans la session de l'application ;

• ligne 21 : on demande au conteneur de fragments d'afficher le fragment désiré ;

1. @Override

2. public boolean onOptionsItemSelected(MenuItem item) { 3. // log

4. if (IS_DEBUG_ENABLED) {

5. Log.d("menu", "onOptionsItemSelected"); 6. }

7. // traitement des options de menu

8. int id = item.getItemId(); 9. switch (id) {

10. case R.id.action_settings: { 11. if (IS_DEBUG_ENABLED) {

12. Log.d("menu", "action_settings selected"); 13. } 14. break; 15. } 16. case R.id.fragment1: { 17. showFragment(0); 18. break; 19. } 20. case R.id.fragment2: { 21. showFragment(1); 22. break; 23. } 24. case R.id.fragment3: { 25. showFragment(2); 26. break; 27. } 28. case R.id.fragment4: { 29. showFragment(3); 30. break; 31. } 32. } 33. // item traité 34. returntrue; 35. }

• lignes 16-31 : gestion des 4 options du menu. Chaque gestionnaire appelle la méthode [showFragment] avec le n° du

fragment à afficher ;

La méthode [showFragment] est la suivante : 1. // l'onglet n° 2

2. private TabLayout.Tab tab2 = null; 3.

4. private void showFragment(int i) {

5. if (i < FRAGMENTS_COUNT && mViewPager.getCurrentItem() != i) { 6. // si le 2ième onglet n'existe pas encore, on le crée

7. if (tab2 == null) {

8. tab2 = tabLayout.newTab(); 9. tabLayout.addTab(tab2); 10. }

11. // on fixe le titre du second onglet

12. tab2.setText(String.format("Fragment n° %s", (i + 1))); 13. // on change le fragment affiché

14. mViewPager.setCurrentItem(i);

15. // le n° du fragment affiché est mis en session

16. session.setNumFragment(i);

17. // on sélectionne l'onglet 2 - ne fait rien si celui-ci est déjà sélectionné

18. tab2.select(); 19. }

20. }

• on se rappelle qu'au départ de l'application, on n'a qu'un onglet ;

ligne 2 : une référence sur l'onglet n° 2, null au départ ;

• ligne 5 : les conditions d'affichage n'ont pas changé par rapport à la version précédente ;

• lignes 7-10 : si l'onglet n° 2 n'existe pas encore, il est créé (ligne 8) et ajouté à la barre d'onglets (ligne 9) ;

• ligne 12 : on met dans le titre du second onglet le n° du fragment qui va être affiché avec une numérotation commençant à

1 ;

• ligne 14 : le fragment désiré est affiché ;

• ligne 16 : son n° est mis dans la session ;

• ligne 18 : l'onglet n° 2 est sélectionné. S'il était déjà sélectionné, rien ne se passera : la méthode [onTabSelected] ne sera pas exécutée. S'il n'était pas déjà sélectionné, la méthode [onTabSelected] va se déclencher. Cette méthode demande alors au conteneur de fragments d'afficher le fragment déjà affiché ligne 14. Un simple test dans la méthode [onTabSelected] évite ce cas :

1. // affichage fragment seulement si c'est nécessaire

2. if (numFragment != mViewPager.getCurrentItem()) { 3. mViewPager.setCurrentItem(numFragment); 4. }

1.12.6 Améliorations

Nous avons désormais une bonne compréhension des fragments, de leur cycle de vie, de la notion d'adjacence de fragments et de leur relation avec la barre d'onglets. Nous avons par ailleurs une architecture robuste qui vient de passer le test de l'exemple 11 :

• une activité et n fragments ;

• tous les fragments étendent la classe [AbstractFragment] ;

• les données à partager entre fragments et entre fragments et activité sont placées dans la classe [Session] ; Nous allons dans un nouveau projet préciser les relations entre activité et fragments par l'ajout d'une interface.