Loupe

Au secours mon ViewPager Android affiche n'importe quoi quand je tourne mon téléphone 😱

Le ViewPager est un composant beaucoup utilisé sur Android : il permet d'afficher plusieurs contenus verticaux (des pages) que l'utilisateur peut retrouver en swippant. Dans cet article nous verrons comment corriger un bug très courant lié à la rotation du téléphone.

Le ViewPager fonctionne à l'aide d'un adapter qui fournit une liste de fragments à afficher ainsi que les titres de chaque page pour les afficher éventuellement dans une TabStrip. Voici un exemple d'utilisation :

IMG_20200905_163801.jpg

Voici un exemple tout bête d'adapter : 

public class MoviesListsPageAdapter : FragmentPagerAdapter {
  public MoviesListsPageAdapter (IntPtr javaReference, JniHandleOwnership transfer) 
  : base (javaReference, transfer) { }
  public MoviesListsPageAdapter (FragmentManager fm) : base (fm) { }
  public override int Count => 5;
  private readonly Dictionary<int, Fragment> _fragments = new Dictionary<int, Fragment> {
     { 0, new MonFragment { MonParam = MediaItemStatus.Watched } },
    { 1, new MonFragment { MonParam = MediaItemStatus.Watchlist } },
  };
  public override Fragment GetItem (int position) => _fragments[position];
  public override ICharSequence GetPageTitleFormatted (int position) {
    switch (position) {
      default:
        case 0:
        return new Java.Lang.String ("mon titre 1");
      case 1:
          return new Java.Lang.String ("mon titre 2");
    }
  }
}

J'ai donc deux fragments "MonFragment" identique auquel je donne un paramètre MonParam. Lors du premier affichage cela fonctionne parfaitement mais lorsque je tourne mon téléphone... ça ne marche plus : toutes les pages sont identiques 😱!

La cause du problème est en réalité ce fameux paramètre passé lors de la création du Fragment. Lorsque l'on change l'orientation du téléphone, le fragment n'est pas redemandé à l'adapter mais restauré par le runtime Android. Tout paramètre non sauvegardé est alors perdu et on se retrouve avec un comportement par défaut... Simple à comprendre mais un peu fastidieux à comprendre lorsque l'on ne sait pas d'où cela provient. 

Heureusement, la correction est facile, il suffit de sauvegarder ces paramètres dans une surcharge de la méthode OnSaveInstanceState : 

public override void OnSaveInstanceState(Bundle outState)
{
  outState.PutInt("MonParam", 
     (int)MonParam);
  base.OnSaveInstanceState(outState);
}

Et de regarder s'il existe ou pas lors de la création de la vue :

public override View OnCreateView 
        (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  var ignore = base.OnCreateView (inflater, container, savedInstanceState);
  if (MediaItemStatus == null && savedInstanceState != null) {
    MonParam =  savedInstanceState.GetInt ("MonParam", "Valeur par défaut";
  }
}

Si vous voulez aller plus loin, sachez qu'on peut aussi combiner ce composant avec un composant BottomNavigationView (article sur le sujet) pour afficher les différents contenus.

Et voilà : happy coding !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus