• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 16/10/2018

Implémentez votre rendu sur tablette

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Précédemment, nous avons pu créer notre première mini-application implémentant des fragments : MyFragmentApp. Vous pouvez retrouver l'ensemble de son code source à cette adresse.

Dans ce chapitre, nous allons voir comment optimiser son affichage sur tablette.

Rendre notre interface dynamique

Dans le chapitre précédent, nous avons vu la manière dont une activité pouvait contenir un fragment de manière statique, c'est-à-dire directement depuis son layout, grâce à la balise <fragment>.

Cependant, cette approche est un peu limitante, notamment dès lors que l'on souhaite ajouter ou supprimer un ou plusieurs fragments à la volée (c'est à dire depuis le code JAVA de notre activité).

Pour pallier ce problème, nous allons aborder maintenant la manière dynamique d'afficher un fragment dans une activité, grâce notamment au FrameLayout et au FragmentManager.

Mais c'est quoi le FrameLayout machin chose ?

Le FrameLayout est un layout qui va nous servir de conteneur pour afficher nos fragments. Ces derniers seront appelés et créés depuis l'activité (via le FragmentManager que nous verrons juste après), puis affichés dans un FrameLayout bien défini.

Afin d'y voir plus clair, un peu de pratique nous fera le plus grand bien ! Nous allons rendre notre interface utilisateur dynamique. Oui oui !

Création des conteneurs FrameLayout

Pour cela, nous allons modifier les deux fichiers layouts de nos activités, activity_main.xml et activity_detail.xml pour y ajouter notre conteneur FrameLayout :

Fichier activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/frame_layout_main"
    android:layout_height="match_parent"
    android:layout_width="match_parent"/>

Fichier activity_detail.xml :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/frame_layout_detail"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Explications : Nous avons ici déclaré un FrameLayout par activité, prenant toute la place disponible sur l'écran.

Affichage des fragments via le FragmentManager

Maintenant que nos conteneurs sont définis, il va falloir afficher nos fragments à l'intérieur : cette responsabilité est attribuée, comme vous l'aurez deviné, au FragmentManager !

Extrait du fichier MainActivity.java :

public class MainActivity extends AppCompatActivity implements MainFragment.OnButtonClickedListener {

    // 1 - Declare main fragment
    private MainFragment mainFragment;

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

        // 2 - Configure and show home fragment
        this.configureAndShowMainFragment();
    }

    ...

    // --------------
    // FRAGMENTS
    // --------------

    private void configureAndShowMainFragment(){
        // A - Get FragmentManager (Support) and Try to find existing instance of fragment in FrameLayout container
        mainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.frame_layout_main);

        if (mainFragment == null) {
            // B - Create new main fragment
            mainFragment = new MainFragment();
            // C - Add it to FrameLayout container
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.frame_layout_main, mainFragment)
                    .commit();
        }
    }
}

Explications :

Nous allons premièrement déclarer notre fragment MainFragment dans une variable ( 1 ), afin d'y avoir accès plus rapidement (mais également pour lui passer des données, comme nous le verrons plus tard).

Puis, nous allons appeler notre méthode fraîchement créée ( 2 ) configureAndShowMainFragment( ) dans le onCreate( ) afin de créer et afficher notre fragment :

  • Ligne A : Grâce à notre FragmentManager, nous allons vérifier s'il existe un fragment de type MainFragment déjà affiché dans notre FrameLayout en utilisant la méthode findFragmentById( ).

  • Ligne B : Si aucun fragment n'est affiché dans notre FrameLayout, nous en créons un nouveau. Pour cela, on instancie un nouvel objet MainFragment.

  • Ligne C : A l'aide de notre FragmentManager, nous allons débuter une Transaction, qui nous permettra d'ajouter notre fragment à l'intérieur de notre FrameLayout.  

Je vous laisse réaliser maintenant la même chose pour l'activité DetailActivity  ;). Attention, pensez bien que celle-ci n'affichera pas le fragment MainFragment mais plutôt DetailFragment :

public class DetailActivity extends AppCompatActivity {

    // 1 - Declare detail fragment
    private DetailFragment detailFragment;

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

        // 2 - Configure and show home fragment
        this.configureAndShowDetailFragment();
    }

    // --------------
    // FRAGMENTS
    // --------------

    private void configureAndShowDetailFragment(){
        // A - Get FragmentManager (Support) and Try to find existing instance of fragment in FrameLayout container
        detailFragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.frame_layout_detail);

        if (detailFragment == null) {
            // B - Create new main fragment
            detailFragment = new DetailFragment();
            // C - Add it to FrameLayout container
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.frame_layout_detail, detailFragment)
                    .commit();
        }
    }
}

Optimiser l'affichage sur tablette

Oui je sais, vous l'attendez avec impatience : l'optimisation de l'affichage de notre application sur tablette ! Pour cela, nous allons combiner deux concepts vaillamment appris durant ce cours, à savoir : les fragments et les quantificateurs.

Pour résumer, nous allons dire à Android d'utiliser un fichier layout différent en mode tablette, qui contiendra deux fragments : MainFragment et DetailFragment. Et comme vous l'aurez deviné, nous allons effectuer cette opération grâce à un quantificateur... mais pas que... ;)

Pour cela, créez un dossier layout-w600dp à la racine de votre dossier res/ et copiez/collez-y le fichier activity_main.xml.

Le quantificateur -w600dp nous permettra de dire à Android de pointer vers ce répertoire gérant les layouts, pour les terminaux dont la largeur fait au moins 600 dp (à partir des tablettes 7 pouces par exemple) : 

                     

 D'accord mais du coup on ne copie pas les autres layouts dedans ? Par exemple activity_detail.xml ?

Et bien non ! On veut juste gérer différemment l'affichage de notre MainActivity en mode tablette.

Maintenant que notre fichier activity_main.xml se trouve bien dans le dossier layout-w600dp, nous allons le modifier afin de faire en sorte qu'il affiche deux fragments au lieu d'un seul. Pour cela, nous ajoutons un autre.... FrameLayout qui contiendra notre second fragment, DetailFragment !

Fichier layout /res/layout-w600dp/activity_main.xml : 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <FrameLayout
        android:id="@+id/frame_layout_main"
        android:layout_weight="30"
        android:layout_width="0px"
        android:layout_height="match_parent"
        />
    <FrameLayout
        android:id="@+id/frame_layout_detail"
        android:layout_weight="70"
        android:layout_width="0px"
        android:layout_height="match_parent"
        />
</LinearLayout>

Explications : 

Nous séparons ici notre écran en deux FrameLayout, l'un à gauche qui prendra 30% de la taille de notre écran et qui contiendra notre fragment MainFragment, et l'autre à droite qui prendra 70% de la taille de notre écran et qui contiendra notre fragment DetailFragment

Et c'est maintenant que ça se complique, car il faut dire à MainActivity d'afficher (en utilisant FragmentManager) notre fragment DetailFragment, uniquement quand il est en mode tablette.

 Extrait du fichier MainActivity.java :

public class MainActivity extends AppCompatActivity implements MainFragment.OnButtonClickedListener {

    ....

    // 1 - Declare detail fragment
    private DetailFragment detailFragment;

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

        ....
        
        // 2 - Configure and show detail fragment
        this.configureAndShowDetailFragment();
    }

    // --------------
    // CallBack
    // --------------

    @Override
    public void onButtonClicked(View view) {
        // 3 - Check if detail fragment is not created or if not visible
        if (detailFragment == null || !detailFragment.isVisible()){
            startActivity(new Intent(this, DetailActivity.class));
        }
    }

    // --------------
    // FRAGMENTS
    // --------------

    ....

    private void configureAndShowDetailFragment(){
        detailFragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.frame_layout_detail);

        //A - We only add DetailFragment in Tablet mode (If found frame_layout_detail)
        if (detailFragment == null && findViewById(R.id.frame_layout_detail) != null) {
            detailFragment = new DetailFragment();
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.frame_layout_detail, detailFragment)
                    .commit();
        }
    }
}

Explications : 

En suivant la même logique qu'en début du cours, nous allons en premier déclarer notre fragment DetailFragment dans une variable ( 1 ), afin d'y avoir accès plus rapidement. Puis nous appellerons ( 2 ) notre méthode configureAndShowDetailFragment( ) depuis notre onCreate( ) afin d'afficher sous certaines conditions notre fragment DetailFragment :

  • Ligne A : Nous vérifions si nous pouvons récupérer le FrameLayout "frame_layout_detail " et ainsi savoir si nous sommes sur une tablette ou non (car "frame_layout_detail " est déclarée uniquement dans le dossier /res/layout-w600dp). 

  • Puis vous connaissez la suite, puisque c'est la même chose que pour le fragment MainFragment ;) , à savoir que le FragmentManager va débuter une Transaction, qui nous permettra d'ajouter notre fragment DetailFragment à l'intérieur de notre FrameLayout "frame_layout_detail ".

Enfin, nous modifions ( 3 ) notre méthode onButtonClicked( ) afin d'afficher DetailActivity (et son fragment DetailFragment) uniquement lorsque nous sommes sur Smartphone.

Lancez maintenant votre application sur tablette (en créant un nouvel émulateur si besoin) : Félicitations ! Vous venez d'optimiser votre application pour un affichage tablette, cool :D.

N'hésitez pas à vous amuser un petit peu avec ce nouvel affichage, en ajoutant peut-être d'autres quantificateurs pour gérer d'autres types de configuration comme par exemple l'orientation en mode paysage, etc... Bref, éclatez-vous, le champ des possibles est infini ! :soleil:

Conclusion

Nous avons enfin vu comment optimiser notre affichage sur tablette en combinant la puissance des quantificateurs à celui des fragments : plus rien ne peut maintenant nous arrêter !

Vous pouvez retrouver le code de l'application MyFragmentApp sur ce lien.

Dans le prochain chapitre, nous allons améliorer la stabilité de cette architecture et gérer ainsi correctement un changement brutal d'orientation (passage du mode portrait en mode paysage).

Exemple de certificat de réussite
Exemple de certificat de réussite