1. Einführung
Das Ökosystem der Android-Geräte entwickelt sich ständig weiter. Von den ersten Tagen der integrierten Hardwaretastaturen bis hin zu den modernen faltbaren Geräten, Tablets und kostenlos anpassbaren Fenstern – Android-Apps liefen noch nie auf einer so vielfältigen Auswahl an Geräten wie heute.
Das ist zwar eine gute Nachricht für Entwickler, aber bestimmte App-Optimierungen sind erforderlich, um die Erwartungen an die Nutzerfreundlichkeit zu erfüllen und für eine hervorragende Nutzerfreundlichkeit auf verschiedenen Bildschirmgrößen zu sorgen. Anstatt jedes neue Gerät einzeln zu unterstützen, können Sie mit einer responsiven/adaptiven Benutzeroberfläche und einer robusten Architektur dafür sorgen, dass Ihre App überall dort, wo Ihre aktuellen und zukünftigen Nutzer sind, auf Geräten jeder Größe und Form gut aussieht und funktioniert.
Die Einführung von kostenlos anpassbaren Android-Umgebungen ist eine gute Möglichkeit, Ihre responsive/adaptive Benutzeroberfläche zu testen, damit sie für jedes Gerät bereit ist. In diesem Codelab erfahren Sie, welche Auswirkungen die Größenänderung hat, und lernen einige Best Practices kennen, mit denen Sie eine App robust und einfach anpassen können.
Umfang
Sie lernen die Auswirkungen der Freiform-Größenanpassung kennen und optimieren eine Android-App, um Best Practices für die Größenanpassung zu demonstrieren. Mit der Anwendung können Sie Folgendes tun:
Kompatibles Manifest haben
- Entfernen Sie Einschränkungen, die verhindern, dass die Größe einer App frei geändert werden kann.
Status bei Änderung der Größe beibehalten
- Der UI-Status wird beim Ändern der Größe mit rememberSaveable beibehalten.
- Unnötiges Duplizieren von Hintergrundarbeiten zum Initialisieren der Benutzeroberfläche vermeiden
Voraussetzungen
- Kenntnisse in der Erstellung einfacher Android-Anwendungen
- Kenntnisse von ViewModel und State in Compose
- Ein Testgerät, das die kostenlose Größenanpassung von Fenstern unterstützt, z. B. eines der folgenden:
- Ein Chromebook mit ADB-Einrichtung
- Ein Tablet, das den Samsung DeX-Modus oder den Produktivitätsmodus unterstützt
- Der Emulator für virtuelle Android-Geräte für Desktop-Computer in Android Studio
Wenn Sie bei der Bearbeitung dieses Codelabs auf Probleme stoßen (z. B. Codefehler, Grammatikfehler oder unklare Formulierungen), melden Sie das Problem bitte über den Link Fehler melden unten links im Codelab.
2. Erste Schritte
Klonen Sie das Repository von GitHub.
git clone https://github.com/android/large-screen-codelabs/
…oder laden Sie eine ZIP-Datei des Repositorys herunter und extrahieren Sie sie.
Projekt importieren
- Android Studio öffnen
- Wählen Sie Import Project (Projekt importieren) oder File > New > Import Project (Datei > Neu > Projekt importieren) aus.
- Wechseln Sie zu dem Ort, an dem Sie das Projekt geklont oder extrahiert haben.
- Öffnen Sie den Ordner resizing (Größenanpassung).
- Öffnen Sie das Projekt im Ordner start. Dieser enthält den Startcode.
App ausprobieren
- App erstellen und ausführen
- Größe der App ändern
Was ist Ihre Meinung dazu?
Je nach Kompatibilität Ihres Testgeräts haben Sie wahrscheinlich festgestellt, dass die Nutzerfreundlichkeit nicht optimal ist. Die Größe der App kann nicht geändert werden und sie wird im ursprünglichen Seitenverhältnis angezeigt. Was genau ist geplant?
Manifesteinschränkungen
Wenn Sie sich die AndroidManifest.xml-Datei der App ansehen, werden Sie feststellen, dass einige Einschränkungen hinzugefügt wurden, die verhindern, dass sich unsere App in einer Umgebung mit kostenlosem Anpassen der Fenstergröße gut verhält.
AndroidManifest.xml
android:maxAspectRatio="1.4"
android:resizeableActivity="false"
android:screenOrientation="portrait">
Entfernen Sie diese drei problematischen Zeilen aus Ihrem Manifest, erstellen Sie die App neu und versuchen Sie es noch einmal auf Ihrem Testgerät. Die App kann jetzt wieder kostenlos skaliert werden. Das Entfernen solcher Einschränkungen aus Ihrem Manifest ist ein wichtiger Schritt bei der Optimierung Ihrer App für die kostenlose Fenstergrößenanpassung.
3. Konfigurationsänderungen bei der Größenanpassung
Wenn die Größe des Fensters Ihrer App geändert wird, wird die Konfiguration Ihrer App aktualisiert. Diese Änderungen haben Auswirkungen auf Ihre App. Wenn Sie sie kennen und darauf vorbereitet sind, können Sie Ihren Nutzern eine optimale Nutzerfreundlichkeit bieten. Die offensichtlichsten Änderungen betreffen die Breite und Höhe des App-Fensters. Diese Änderungen haben jedoch auch Auswirkungen auf das Seitenverhältnis und die Ausrichtung.
Konfigurationsänderungen beobachten
Wenn Sie diese Änderungen in einer App, die mit dem Android-Ansichtssystem erstellt wurde, selbst sehen möchten, können Sie View.onConfigurationChanged überschreiben. In Jetpack Compose haben wir Zugriff auf LocalConfiguration.current, das automatisch aktualisiert wird, wenn View.onConfigurationChanged aufgerufen wird.
Wenn Sie diese Konfigurationsänderungen in Ihrer Beispiel-App sehen möchten, fügen Sie Ihrer App ein Composable hinzu, das Werte aus LocalConfiguration.current anzeigt, oder erstellen Sie ein neues Beispielprojekt mit einem solchen Composable. Eine Beispiel-UI für die Anzeige dieser Informationen könnte so aussehen:
val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation ==
Configuration.ORIENTATION_PORTRAIT
val screenLayoutSize =
when (configuration.screenLayout and
Configuration.SCREENLAYOUT_SIZE_MASK) {
SCREENLAYOUT_SIZE_SMALL -> "SCREENLAYOUT_SIZE_SMALL"
SCREENLAYOUT_SIZE_NORMAL -> "SCREENLAYOUT_SIZE_NORMAL"
SCREENLAYOUT_SIZE_LARGE -> "SCREENLAYOUT_SIZE_LARGE"
SCREENLAYOUT_SIZE_XLARGE -> "SCREENLAYOUT_SIZE_XLARGE"
else -> "undefined value"
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text("screenWidthDp: ${configuration.screenWidthDp}")
Text("screenHeightDp: ${configuration.screenHeightDp}")
Text("smallestScreenWidthDp: ${configuration.smallestScreenWidthDp}")
Text("orientation: ${if (isPortrait) "portrait" else "landscape"}")
Text("screenLayout SIZE: $screenLayoutSize")
}
Eine Beispielimplementierung finden Sie im Projektordner observing-configuration-changes. Fügen Sie dies der Benutzeroberfläche Ihrer App hinzu, führen Sie sie auf Ihrem Testgerät aus und beobachten Sie, wie sich die Benutzeroberfläche ändert, wenn sich die Konfiguration Ihrer App ändert.

Durch diese Änderungen an der Konfiguration Ihrer App können Sie schnell simulieren, wie sich die App verhält, wenn sie auf einem kleinen Smartphone im Splitscreen-Modus und auf einem Tablet oder Computer im Vollbildmodus verwendet wird. So können Sie nicht nur das Layout Ihrer App auf verschiedenen Bildschirmen testen, sondern auch, wie gut Ihre App mit schnellen Konfigurationsänderungen umgehen kann.
4. Aktivitätslebenszyklusereignisse protokollieren
Eine weitere Folge der kostenlosen Fenstergrößenanpassung für Ihre App sind die verschiedenen Änderungen am Activity-Lebenszyklus, die für Ihre App auftreten. Wenn Sie diese Änderungen in Echtzeit sehen möchten, fügen Sie der Methode onCreate einen Lebenszyklus-Observer hinzu und protokollieren Sie jedes neue Lebenszyklus-Ereignis, indem Sie onStateChanged überschreiben.
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Log.d("resizing-codelab-lifecycle", "$event was called")
}
})
Führen Sie Ihre App mit dieser Protokollierung noch einmal auf Ihrem Testgerät aus und sehen Sie sich logcat an, während Sie versuchen, Ihre App zu minimieren und wieder in den Vordergrund zu holen.
Beachten Sie, dass Ihre App pausiert wird, wenn sie minimiert wird, und dann wieder fortgesetzt wird, wenn sie in den Vordergrund geholt wird. Das hat Auswirkungen auf Ihre App, die Sie im nächsten Abschnitt dieses Codelabs untersuchen werden.

Sehen Sie sich nun Logcat an, um zu sehen, welche Aktivitäts-Lifecycle-Callbacks aufgerufen werden, wenn Sie die Größe Ihrer App von der kleinstmöglichen auf die größtmögliche ändern.
Je nach Testgerät können Sie unterschiedliche Verhaltensweisen beobachten. Wahrscheinlich haben Sie jedoch festgestellt, dass Ihre Aktivität zerstört und neu erstellt wird, wenn sich die Größe des App-Fensters erheblich ändert, nicht aber, wenn sie sich nur geringfügig ändert. Das liegt daran, dass ab API 24 nur bei erheblichen Größenänderungen Activity neu erstellt wird.
Sie haben einige der häufigen Konfigurationsänderungen gesehen, die in einer Umgebung mit kostenlosem Fenstermodus zu erwarten sind. Es gibt aber noch weitere Änderungen, die Sie beachten sollten. Wenn Sie beispielsweise einen externen Monitor an Ihr Testgerät angeschlossen haben, können Sie sehen, dass Ihre Activity zerstört und neu erstellt wird, um Konfigurationsänderungen wie die Displaydichte zu berücksichtigen.
Um einen Teil der Komplexität im Zusammenhang mit den Konfigurationsänderungen zu abstrahieren, verwenden Sie APIs auf höherer Ebene wie WindowSizeClass, um Ihre adaptive Benutzeroberfläche zu implementieren. Weitere Informationen finden Sie auch unter Verschiedene Bildschirmgrößen unterstützen.
5. Kontinuität – Internen Status von Composables beibehalten, wenn die Größe geändert wird
Im vorherigen Abschnitt haben Sie einige der Konfigurationsänderungen gesehen, die Ihre App in einer Umgebung mit kostenloser Fenstergrößenanpassung erwarten kann. In diesem Abschnitt sorgen Sie dafür, dass der UI-Zustand Ihrer App während dieser Änderungen kontinuierlich bleibt.
Sorgen Sie zuerst dafür, dass die zusammensetzbare Funktion NavigationDrawerHeader (in ReplyHomeScreen.kt) maximiert wird, wenn darauf geklickt wird, sodass die E-Mail-Adresse angezeigt wird.
@Composable
private fun NavigationDrawerHeader(
modifier: Modifier = Modifier
) {
var showDetails by remember { mutableStateOf(false) }
Column(
modifier = modifier.clickable {
showDetails = !showDetails
}
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
ReplyLogo(
modifier = Modifier
.size(dimensionResource(R.dimen.reply_logo_size))
)
ReplyProfileImage(
drawableResource = LocalAccountsDataProvider
.userAccount.avatar,
description = stringResource(id = R.string.profile),
modifier = Modifier
.size(dimensionResource(R.dimen.profile_image_size))
)
}
AnimatedVisibility (showDetails) {
Text(
text = stringResource(id = LocalAccountsDataProvider
.userAccount.email),
style = MaterialTheme.typography.labelMedium,
modifier = Modifier
.padding(
start = dimensionResource(
R.dimen.drawer_padding_header),
end = dimensionResource(
R.dimen.drawer_padding_header),
bottom = dimensionResource(
R.dimen.drawer_padding_header)
),
)
}
}
}
Nachdem Sie den maximierbaren Header in Ihre App eingefügt haben,
- die App auf Ihrem Testgerät ausführen
- Tippen Sie auf den Header, um ihn zu maximieren.
- Versuchen Sie, die Größe des Fensters zu ändern.
Sie werden feststellen, dass der Header bei einer erheblichen Größenänderung seinen Status verliert.

Der UI-Status geht verloren, da remember zwar hilft, den Status bei Recompositionen beizubehalten, aber nicht bei der Neuerstellung von Aktivitäten oder Prozessen. Häufig wird State Hoisting verwendet, um den Status an den Aufrufer einer Composable-Funktion zu übertragen und Composables statuslos zu machen. Dadurch kann dieses Problem vollständig vermieden werden. Dennoch können Sie remember an Stellen verwenden, an denen der Status von UI-Elementen innerhalb von zusammensetzbaren Funktionen beibehalten werden soll.
Ersetzen Sie remember durch rememberSaveable, um diese Probleme zu beheben. Das funktioniert, weil rememberSaveable den gespeicherten Wert in savedInstanceState speichert und wiederherstellt. Ändern Sie remember in rememberSaveable, führen Sie Ihre App auf dem Testgerät aus und versuchen Sie noch einmal, die Größe der App zu ändern. Sie werden feststellen, dass der Status des maximierbaren Headers beim Ändern der Größe beibehalten wird, wie vorgesehen.
6. Unnötige Wiederholungen von Hintergrundarbeiten vermeiden
Sie haben gesehen, wie Sie mit rememberSaveable den internen UI-Status von Composables bei Konfigurationsänderungen beibehalten können, die häufig durch das kostenlose Ändern der Fenstergröße auftreten. Oft sollte eine App jedoch UI-Status und ‑Logik von Composables trennen. Den Status in ein ViewModel zu verschieben ist eine der besten Möglichkeiten, den Status beim Ändern der Größe beizubehalten. Wenn Sie den Status in ein ViewModel verschieben, können Probleme mit zeitaufwendigen Hintergrundaufgaben auftreten, z. B. mit intensivem Dateisystemzugriff oder Netzwerkaufrufen, die zum Initialisieren des Bildschirms erforderlich sind.
Wenn Sie sich ein Beispiel für die Arten von Problemen ansehen möchten, die auftreten können, fügen Sie der Methode initializeUIState in ReplyViewModel eine Log-Anweisung hinzu.
fun initializeUIState() {
Log.d("resizing-codelab", "initializeUIState() called in the viewmodel")
val mailboxes: Map<MailboxType, List<Email>> =
LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
_uiState.value =
ReplyUiState(
mailboxes = mailboxes,
currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
?: LocalEmailsDataProvider.defaultEmail
)
}
Führen Sie die App jetzt auf Ihrem Testgerät aus und versuchen Sie, das Fenster der App mehrmals zu vergrößern und zu verkleinern.
Wenn Sie sich Logcat ansehen, werden Sie feststellen, dass die Initialisierungsmethode mehrmals ausgeführt wurde. Das kann problematisch sein, wenn Sie bestimmte Vorgänge nur einmal ausführen möchten, um die Benutzeroberfläche zu initialisieren. Die zusätzlichen Netzwerkaufrufe, Datei-E/A oder andere Vorgänge können die Leistung des Geräts beeinträchtigen und andere unbeabsichtigte Probleme verursachen.
Entfernen Sie den Aufruf von initializeUIState() aus der Methode onCreate() Ihrer Aktivität, um unnötige Hintergrundarbeiten zu vermeiden. Initialisieren Sie die Daten stattdessen in der init-Methode des ViewModel. So wird dafür gesorgt, dass die Initialisierungsmethode nur einmal ausgeführt wird, wenn ReplyViewModel zum ersten Mal instanziiert wird:
init {
initializeUIState()
}
Wenn Sie die App noch einmal ausführen, sehen Sie, dass die unnötige simulierte Initialisierungsaufgabe nur einmal ausgeführt wird, unabhängig davon, wie oft Sie das Fenster der App in der Größe ändern. Das liegt daran, dass ViewModels über den Lebenszyklus der Activity hinaus bestehen bleiben. Indem wir den Initialisierungscode nur einmal beim Erstellen von ViewModel ausführen, trennen wir ihn von allen Activity-Neuerstellungen und vermeiden unnötige Arbeit. Wenn es sich tatsächlich um einen teuren Serveraufruf oder einen intensiven Datei-I/O-Vorgang zum Initialisieren der Benutzeroberfläche handelt, würden Sie erhebliche Ressourcen sparen und die Nutzerfreundlichkeit verbessern.
7. Das wars auch schon!
Geschafft! Gut gemacht! Sie haben nun einige Best Practices implementiert, damit Android-Apps auf ChromeOS und in anderen Umgebungen mit mehreren Fenstern und Bildschirmen gut skaliert werden.
Beispiel-Quellcode
Repository von GitHub klonen
git clone https://github.com/android/large-screen-codelabs/
…oder laden Sie eine ZIP-Datei des Repositorys herunter und extrahieren Sie sie.