Responsive Benutzeroberfläche und Navigation
Um den Nutzern die bestmögliche Navigation zu bieten, solltest du eine Navigations-UI bereitstellen, die auf die Breite, Höhe und kleinste Breite des jeweiligen Geräts zugeschnitten ist. Sie können z. B. eine untere App-Leiste, eine immer verfügbare oder minimierbare Navigationsleiste, eine Leiste oder eine völlig neue Art der Navigationsleiste verwenden – je nach verfügbarem Bildschirmbereich und dem einzigartigen Stil Ihrer App.
Der Leitfaden zur Produktarchitektur für Material Design enthält zusätzlichen Kontext und weitere Überlegungen zum Erstellen einer responsiven UI, also einer UI, die sich dynamisch an Umgebungsänderungen anpasst. Einige Beispiele für umgebungsbezogene Änderungen sind Anpassungen der Breite, Höhe, Ausrichtung und Spracheinstellungen des Nutzers. Diese Umgebungseigenschaften werden zusammen als die Konfiguration des Geräts bezeichnet.
Wenn sich eines oder mehrere dieser Eigenschaften während der Laufzeit ändern, reagiert das Android-Betriebssystem, indem die Aktivitäten und Fragmente der App gelöscht und anschließend neu erstellt werden. Zur Unterstützung einer responsiven Benutzeroberfläche unter Android sollten Sie daher am besten darauf achten, gegebenenfalls Ressourcenkonfigurationsqualifizierer zu verwenden und fest codierte Layoutgrößen zu vermeiden.
Globale Navigation in einer responsiven UI implementieren
Die Implementierung der globalen Navigation als Teil einer responsiven UI beginnt mit der Aktivität, die das Navigationsdiagramm hostet. Ein praktisches Beispiel finden Sie im Navigationscodelab.
Im Codelab wird ein NavigationView
verwendet, um das Navigationsmenü aufzurufen (siehe Abbildung 2). Beim Ausführen auf einem Gerät, das mit einer Breite von mindestens 960 dp gerendert wird, ist NavigationView
immer auf dem Bildschirm zu sehen.
Andere Gerätegrößen und ‐ausrichtungen wechseln bei Bedarf dynamisch zwischen DrawerLayout
oder BottomNavigationView
.
Sie können dieses Verhalten implementieren, indem Sie drei verschiedene Layouts erstellen, wobei jedes Layout die gewünschten Navigationselemente und Ansichtshierarchie basierend auf der aktuellen Gerätekonfiguration definiert.
Die Konfiguration, für die die einzelnen Layouts gelten, wird durch die Verzeichnisstruktur bestimmt, in der sich die Layoutdatei befindet. Die Layoutdatei NavigationView
befindet sich beispielsweise im Verzeichnis 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>
Die untere Navigationsansicht befindet sich im Verzeichnis 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>
Das Leistenlayout befindet sich im Verzeichnis res/layout
. Verwenden Sie dieses Verzeichnis für Standardlayouts ohne konfigurationsspezifische Qualifizierer:
<!-- 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 folgt bei der Bestimmung, welche Ressourcen angewendet werden sollen, einer Rangfolge. Speziell in diesem Beispiel hat -w960dp
(oder verfügbare Breite >= 960 dp) Vorrang vor -h470dp
(oder verfügbare Höhe >= 470). Wenn die Gerätekonfiguration keine dieser Bedingungen erfüllt, wird die Standardlayoutressource (res/layout/navigation_activity.xml
) verwendet.
Bei der Verarbeitung von Navigationsereignissen m��ssen Sie nur die Ereignisse verbinden, die den aktuell vorhandenen Widgets entsprechen, wie im folgenden Beispiel gezeigt.
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); } } }
Wenn sich die Gerätekonfiguration ändert, löscht Android die Aktivität aus der vorherigen Konfiguration zusammen mit den zugehörigen Ansichten. Diese Konfiguration ist nicht explizit konfiguriert. Anschließend wird die Aktivität mit Ressourcen neu erstellt, die für die neue Konfiguration vorgesehen sind. Die Aktivität, die gelöscht und neu erstellt wird, verbindet dann automatisch die richtigen globalen Navigationselemente in onCreate()
.
Alternativen zu Layouts mit geteilter Ansicht in Betracht ziehen
Split-View-Layouts oder Master-/Detail-Layouts waren früher eine sehr beliebte und empfohlene Methode für die Entwicklung von Designs für Tablets und andere Geräte mit großen Bildschirmen.
Seit der Einführung der Android-Tablets ist die Zahl der Geräte inzwischen rasant gewachsen. Ein Faktor, der den Designbereich für Geräte mit großen Bildschirmen erheblich beeinflusst hat, war die Einführung von Mehrfenstermodi, insbesondere Freiformfenstern, deren Größe vollständig angepasst werden kann, wie z. B. auf ChromeOS-Geräten. Dadurch wird die Reaktion auf jeden Bildschirm Ihrer App erheblich betont, anstatt die Navigationsstruktur an die Bildschirmgröße anzupassen.
Es ist zwar möglich, mithilfe der Navigationsbibliothek eine Layoutoberfläche mit geteilter Ansicht zu implementieren, Sie sollten jedoch andere Alternativen in Betracht ziehen.
Zielnamen
Wenn Sie in Ihrer Grafik Zielnamen mit dem Attribut android:label
angeben, müssen Sie immer Ressourcenwerte verwenden, damit Ihre Inhalte weiterhin lokalisiert werden können.
<navigation ...>
<fragment
android:id="@+id/my_dest"
android:name="com.example.MyFragment"
android:label="@string/my_dest_label"
tools:layout="@layout/my_fragment" />
...
Mit Ressourcenwerten werden auf Ihre Ziele automatisch die am besten geeigneten Ressourcen angewendet, wenn sich Ihre Konfiguration ändert.