Interface utilisateur et navigation responsives
Pour offrir la meilleure expérience de navigation possible à vos utilisateurs, vous devez fournir une interface utilisateur de navigation adaptée à la largeur, à la hauteur et à la largeur minimale de l'appareil. Vous pouvez utiliser une barre d'application inférieure, un panneau de navigation toujours présent ou réductible, un rail ou peut-être quelque chose de totalement nouveau en fonction de l'espace disponible à l'écran et du style unique de votre application.
Le guide concernant l'architecture des produits de Material Design fournit du contexte et des considérations supplémentaires pour créer une interface utilisateur responsive, c'est-à-dire une interface qui s'adapte de manière dynamique aux changements de l'environnement. Voici quelques exemples de changements de l'environnement : ajustements de largeur, de hauteur, d'orientation et de préférence linguistique de l'utilisateur. Ces propriétés d'environnement sont collectivement désignées sous le nom de configuration de l'appareil.
Lorsqu'une ou plusieurs de ces propriétés changent au moment de l'exécution, le système d'exploitation Android répond en détruisant, puis en recréant les activités et les fragments de votre application. Par conséquent, le meilleur moyen de gérer une interface utilisateur responsive sur Android est de vous assurer que vous utilisez des qualificatifs de configuration de ressources, le cas échéant, et que vous évitez d'utiliser des tailles de mise en page codées en dur.
Implémenter la navigation globale dans une interface utilisateur responsive
L'implémentation de la navigation globale dans une interface utilisateur responsive commence par l'activité qui héberge votre graphique de navigation. Pour un exemple pratique, consultez l'atelier de programmation sur la navigation.
L'atelier de programmation utilise une NavigationView
pour afficher le menu de navigation, comme illustré dans la figure 2. Sur un appareil qui affiche une largeur d'au moins 960 dp, cette NavigationView
s'affiche toujours à l'écran.
Si nécessaire, les autres tailles et orientations d'appareils alternent de manière dynamique entre DrawerLayout
et BottomNavigationView
.
Vous pouvez implémenter ce comportement en créant trois mises en page différentes, où chaque mise en page définit les éléments de navigation souhaités et la hiérarchie des vues en fonction de la configuration actuelle de l'appareil.
La configuration à laquelle s'applique chaque mise en page est déterminée par la structure du répertoire dans lequel le fichier de mise en page est placé. Par exemple, le fichier de mise en page NavigationView
se trouve dans le répertoire res/layout-w960dp
.
<!-- res/layout-w960dp/navigation_activity.xml -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
app:elevation="0dp"
app:headerLayout="@layout/nav_view_header"
app:menu="@menu/nav_drawer_menu" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_toEndOf="@id/nav_view"
android:background="?android:attr/listDivider" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/nav_view"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
android:layout_toEndOf="@id/nav_view"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</RelativeLayout>
La vue de navigation inférieure se trouve dans le répertoire res/layout-h470dp
:
<!-- res/layout-h470dp/navigation_activity.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_nav_menu" />
</LinearLayout>
La mise en page avec panneau se trouve dans le répertoire res/layout
. Utilisez ce répertoire pour les mises en page par défaut sans qualificatifs propres à la configuration :
<!-- res/layout/navigation_activity.xml -->
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.codelabs.navigation.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/nav_drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
Android suit un ordre de priorité pour déterminer les ressources à appliquer. Pour cet exemple, -w960dp
(la largeur disponible est supérieure ou égale à 960 dp) est prioritaire par rapport à -h470dp
(la hauteur disponible est supérieure ou égale à 470). Si la configuration de l'appareil ne correspond à aucune de ces conditions, la ressource de mise en page par défaut (res/layout/navigation_activity.xml
) est utilisée.
Pour gérer les événements de navigation, vous ne devez connecter que les événements correspondant aux widgets existants, comme illustré dans l'exemple suivant.
Kotlin
class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration : AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.navigation_activity) val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout) appBarConfiguration = AppBarConfiguration( setOf(R.id.home_dest, R.id.deeplink_dest), drawerLayout) ... // Initialize the app bar with the navigation drawer if present. // If the drawerLayout is not null here, a Navigation button will be added // to the app bar whenever the user is on a top-level destination. setupActionBarWithNavController(navController, appBarConfig) // Initialize the NavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. val sideNavView = findViewById<NavigationView>(R.id.nav_view) sideNavView?.setupWithNavController(navController) // Initialize the BottomNavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view) bottomNav?.setupWithNavController(navController) ... } ... }
Java
public class MainActivity extends AppCompatActivity { private AppBarConfiguration appBarConfiguration; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.navigation_activity); NavHostFragment host = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.my_nav_host_fragment); NavController navController = host.getNavController(); DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); appBarConfiguration = new AppBarConfiguration.Builder( R.id.home_dest, R.id.deeplink_dest) .setDrawerLayout(drawerLayout) .build(); // Initialize the app bar with the navigation drawer if present. // If the drawerLayout is not null here, a Navigation button will be added to // the app bar whenever the user is on a top-level destination. NavigationUI.setupActionBarWithNavController( this, navController, appBarConfiguration); // Initialize the NavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. NavigationView sideNavView = findViewById(R.id.nav_view); if(sideNavView != null) { NavigationUI.setupWithNavController(sideNavView, navController); } // Initialize the BottomNavigationView if it is present, // so that clicking an item takes // the user to the appropriate destination. BottomNavigationView bottomNav = findViewById(R.id.bottom_nav_view); if(bottomNav != null) { NavigationUI.setupWithNavController(bottomNav, navController); } } }
Si la configuration de l'appareil change, Android détruit l'activité de la configuration précédente ainsi que les vues associées, sauf si vous avez indiqué explicitement dans la configuration que ce ne devait pas être le cas. Android recrée ensuite l'activité avec des ressources conçues pour la nouvelle configuration. L'activité est détruite et recréée, puis associe automatiquement les bons éléments de navigation globale dans onCreate()
.
Envisager des alternatives aux mises en page avec vue fractionnée
Les mises en page avec vue fractionnée, ou mises en page maître/détail, étaient autrefois un moyen très populaire et recommandé pour concevoir des applications pour des tablettes et d'autres appareils à grand écran.
Depuis le lancement des tablettes Android, l'écosystème d'appareils s'est développé rapidement. L'un des facteurs qui ont considérablement influencé l'espace de conception pour les appareils à grand écran est l'introduction des modes multifenêtre, en particulier les fenêtres au format libre entièrement redimensionnables, comme celles des appareils ChromeOS. Il est alors beaucoup plus important de faire en sorte que chaque écran de votre application soit responsif, plutôt que de modifier votre structure de navigation en fonction de la taille de l'écran.
Bien qu'il soit possible de mettre en œuvre une interface de mise en page avec affichage fractionné à l'aide de la bibliothèque Navigation, nous vous recommandons d'envisager d'autres solutions.
Noms de destination
Si vous fournissez des noms de destination dans votre graphique à l'aide de l'attribut android:label
, veillez à toujours utiliser des valeurs de ressource afin que votre contenu puisse toujours être localisé.
<navigation ...>
<fragment
android:id="@+id/my_dest"
android:name="com.example.MyFragment"
android:label="@string/my_dest_label"
tools:layout="@layout/my_fragment" />
...
Avec les valeurs de ressource, les ressources les plus appropriées s'appliquent automatiquement à vos destinations à chaque modification de votre configuration.