Obsługa zmian konfiguracji

Elastyczny interfejs i nawigacja

Aby zapewnić użytkownikom maksymalną wygodę nawigacji, należy: udostępnić interfejs użytkownika dostosowany do szerokości, wysokości mają najmniejszą szerokość ekranu urządzenia użytkownika. Możesz użyć atrybutu dolny pasek aplikacji, zawsze dostępna lub zwijana panelu nawigacji, kolej, albo zupełnie coś nowego, opartego na dostępnej przestrzeni do Twojej aplikacji.

przykłady poręczy, szuflad nawigacji i dolnego paska aplikacji
Rysunek 1. Przykłady: szyna, panele nawigacji i na dolnym pasku aplikacji.

Styl Material Design przewodnik po architekturze usługi zawiera dodatkowy kontekst i kwestie związane z tworzeniem elastycznego interfejsu użytkownika, czyli interfejs, który dynamicznie dostosowuje się do zmian środowiskowych. Kilka przykładów zmiany środowiskowe obejmują zmianę szerokości, wysokości, orientacji ustawienia języka użytkownika. Te właściwości środowiskowe są łącznie nazywaną konfiguracją urządzenia.

Gdy co najmniej jedna z tych właściwości zmienia się w czasie działania, system operacyjny Android reaguje autor: niszczenie i odtwarzanie działań i fragmentów w aplikacji. Dlatego najlepszą obsługę elastycznego interfejsu na Androidzie upewnij się, że używasz kwalifikatory konfiguracji zasobów w stosownych przypadkach, unikania stosowania rozmiarów układów zakodowanych na stałe.

Wdrażanie globalnej nawigacji w elastycznym interfejsie

Implementacja globalnej nawigacji w elastycznym interfejsie zaczyna się od aktywność hostującą wykres nawigacji. Zapoznaj się z praktycznym przykładem, na Ćwiczenie z programowania dotyczące nawigacji. Ćwiczenie w Codelabs korzysta z interfejsu NavigationView aby wyświetlić menu nawigacyjne, tak jak na ilustracji 2. Po uruchomieniu na urządzeniu który wyświetla się o szerokości co najmniej 960 dp, NavigationView to zawsze na ekranie.

ćwiczenie w Codelabs korzysta z widoku nawigacji, który jest zawsze widoczny
            gdy szerokość urządzenia wynosi co najmniej 960 dp.
Rysunek 2. Ćwiczenia z programowania dotyczące nawigacji NavigationView, aby wyświetlić menu nawigacyjne.

Inne rozmiary i orientacje urządzeń dynamicznie przełączają się między DrawerLayout lub BottomNavigationView w razie potrzeby.

z dolnym widokiem nawigacji i układem szuflady, które służą do nawigacji.
            w razie potrzeby w mniejszych układach urządzeń.
Rysunek 3. Ćwiczenia z programowania dotyczące nawigacji BottomNavigationView i DrawerLayout do wyświetlenia w menu nawigacyjnym na mniejszych urządzeniach.

Możesz wdrożyć to rozwiązanie, tworząc 3 różne układy, z których każdy określa wymagane elementy nawigacyjne i hierarchię widoków na podstawie bieżącej konfiguracji urządzenia.

Konfiguracja, do której ma zastosowanie każdy układ, jest określana przez katalog w której jest umieszczony plik szablonu. Na przykład NavigationView w katalogu 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>

Dolny widok nawigacji znajduje się w katalogu 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>

Układ panelu znajduje się w katalogu res/layout. Użyj tego katalogu do układy domyślne bez kwalifikatorów dotyczących konkretnej konfiguracji:

<!-- 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 postępuje zgodnie z kolejność pierwszeństwa przy wyborze zasobów, które warto zastosować. Dotyczy tego przykładu: -w960dp (lub dostępna szerokość >= 960 dp) ma pierwszeństwo przed kolumną -h470dp (lub dostępna) wysokość >= 470). Jeśli konfiguracja urządzenia nie pasuje do tych, warunków, potem zasób domyślnego układu (res/layout/navigation_activity.xml).

Przy obsłudze zdarzeń nawigacji musisz podać tylko te zdarzenia, które odpowiadają do widżetów, które są obecnie dostępne, jak pokazano na przykładzie poniżej.

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);
       }

   }
}

Jeśli konfiguracja urządzenia się zmieni, o ile wyraźnie nie zaznaczono inaczej, skonfigurowanej w inny sposób, Android niszczy aktywność z poprzedniej konfiguracji powiązanych widoków. Następnie odtwarza to działanie za pomocą zasobów przeznaczonych do nową konfigurację. Działanie, zniszczenie i odtworzenie, automatycznie tworzy odpowiednie elementy globalnej nawigacji w onCreate().

Zastanów się nad alternatywami dla układów z podzielonym widokiem

Układy z widokiem dzielonym, czyli układy główne/szczegółowe, były kiedyś bardzo popularny i zalecany sposób projektowania na tablety i inne duże ekrany urządzenia.

Od czasu wprowadzenia tabletów z Androidem ekosystem urządzeń stale się rozwijał. bardzo szybko. Jednym z czynników, które w istotny sposób wpłynęły na architekturę dużych Urządzenia z ekranami zostały wprowadzone w trybach wielu okien. dowolnych okien, których rozmiar można w pełni zmieniać, np. na urządzeniach z ChromeOS. Dzięki temu każdy ekran aplikacji, responsywną, zamiast zmieniać strukturę nawigacji w zależności od ekranu rozmiaru.

Chociaż można wdrożyć interfejs układu podzielonego widoku, wykorzystując w bibliotece nawigacji, rozważ inne alternatywy.

Nazwy miejsc docelowych

Jeśli nazwy miejsc docelowych podajesz na wykresie za pomocą atrybutu android:label pamiętaj, aby zawsze używać wartości zasobów, dzięki czemu treść będzie nadal i zlokalizowania.

<navigation ...>
    <fragment
        android:id="@+id/my_dest"
        android:name="com.example.MyFragment"
        android:label="@string/my_dest_label"
        tools:layout="@layout/my_fragment" />
    ...

Dzięki wartościom zasobów Twoje miejsca docelowe automatycznie zasobów stosowanych przy każdej zmianie konfiguracji.