1. Einführung
Die Welt der Android-Geräte entwickelt sich ständig weiter. Von den Anfängen der integrierten Hardwaretastaturen bis hin zur modernen Landschaft von klappbaren Geräten, faltbaren Geräten, Tablets und Fenstern mit kostenloser Größe, deren Größe angepasst werden kann – Android-Apps wurden noch nie auf einer Vielzahl von Geräten ausgeführt als heute.
Das sind zwar tolle Neuigkeiten für Entwickler, aber bestimmte App-Optimierungen sind erforderlich, um die Erwartungen an die Nutzerfreundlichkeit zu erfüllen und für eine hervorragende Nutzererfahrung bei verschiedenen Bildschirmgrößen zu sorgen. Anstatt jedes neue Gerät einzeln anzusprechen, können eine responsive/adaptive Benutzeroberfläche und eine stabile Architektur dazu beitragen, dass Ihre App überall gut aussieht und funktioniert – auf Geräten jeder Größe und Form!
Mit der Einführung von Android-Umgebungen in freiem Format können Sie Ihre reaktionsschnelle/adaptive Benutzeroberfläche hervorragend testen, um sie auf jedes Gerät vorzubereiten. In diesem Code-Lab lernen Sie die Auswirkungen der Größenanpassung kennen und erfahren, wie Sie einige Best Practices implementieren, um die Größenanpassung einer App stabil und einfach zu gestalten.
Umfang
Sie lernen die Auswirkungen der Größenanpassung im freien Format kennen und optimieren eine Android-App, um Best Practices für die Größenanpassung zu demonstrieren. Mit der Anwendung können Sie Folgendes tun:
Sie haben ein kompatibles Manifest.
- Einschränkungen entfernen, die verhindern, dass die Größe einer App frei angepasst werden kann
Status beim Ändern der Größe beibehalten
- Behält den Status der Benutzeroberfläche bei, wenn seine Größe mit rememberSaveable geändert wird
- Vermeiden Sie es, Hintergrundarbeiten unnötig zu duplizieren, um die UI zu initialisieren.
Voraussetzungen
- Kenntnisse im Erstellen grundlegender Android-Apps
- Kenntnisse zu ViewModel und State in Compose
- Ein Testgerät, das eine Größenanpassung im freien Format unterstützt, z. B.:
- Chromebook mit ADB-Einrichtung
- Ein Tablet, das den Samsung DeX-Modus oder Produktivitätsmodus unterstützt
- Der Emulator für virtuelle Android-Desktopgeräte in Android Studio
Wenn beim Durcharbeiten dieses Codelabs Probleme auftreten (Code-Fehler, Grammatikfehler, unklare Formulierungen usw.), melden Sie sie bitte über den Link Fehler melden links unten im Codelab.
2. Erste Schritte
Klonen Sie das Repository von GitHub.
git clone https://github.com/android/large-screen-codelabs/
...oder eine ZIP-Datei des Repositorys herunterladen und extrahieren
Projekt importieren
- Android Studio öffnen
- Wählen Sie Projekt importieren oder Datei -> Neu -> Projekt importieren aus.
- Zu dem Ort gehen, an dem Sie das Projekt geklont oder extrahiert haben
- Öffnen Sie den Ordner mit der Größenanpassung.
- Öffnen Sie das Projekt im Ordner start. Enthält den Startcode.
App ausprobieren
- Anwendung erstellen und ausführen
- Ändern Sie die Größe der App
Was ist Ihre Meinung dazu?
Je nachdem, welche Kompatibilität Ihr Testgerät unterstützt, haben Sie wahrscheinlich bemerkt, dass die Nutzererfahrung nicht ideal ist. Die Größe der App kann nicht angepasst werden und das ursprüngliche Seitenverhältnis bleibt erhalten. 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 die App in einer Umgebung mit kostenloser Fenstergröße richtig funktioniert.
AndroidManifest.xml
android:maxAspectRatio="1.4"
android:resizeableActivity="false"
android:screenOrientation="portrait">
Entferne diese drei problematischen Zeilen aus deinem Manifest, erstelle die App neu und versuche es noch einmal auf deinem Testgerät. Sie werden feststellen, dass die Größenanpassung für die App nicht mehr eingeschränkt ist. Das Entfernen solcher Einschränkungen aus Ihrem Manifest ist ein wichtiger Schritt bei der Optimierung Ihrer App für die Größenanpassung des Fensters im freien Format.
3. Konfigurationsänderungen bei Größenanpassung
Wenn die Größe des Fensters Ihrer App geändert wird, wird auch die Konfiguration der App aktualisiert. Diese Updates wirken sich auf deine App aus. Wenn Sie diese verstehen und antizipieren, kann dies zu einer positiven Nutzererfahrung beitragen. Die offensichtlichsten Änderungen sind die Breite und Höhe Ihres App-Fensters. Diese Änderungen wirken sich jedoch auch auf das Seitenverhältnis und die Ausrichtung aus.
Konfigurationsänderungen beobachten
Wenn Sie sehen möchten, wie diese Änderungen in einer mit dem Android-Ansichtssystem erstellten App erfolgen, können Sie View.onConfigurationChanged
überschreiben. In Jetpack Compose haben wir Zugriff auf LocalConfiguration.current
, das bei jedem Aufruf von View.onConfigurationChanged
automatisch aktualisiert wird.
Um diese Konfigurationsänderungen in Ihrer Beispiel-App zu sehen, fügen Sie Ihrer App eine zusammensetzbare Funktion hinzu, die Werte aus LocalConfiguration.current
anzeigt, oder erstellen Sie ein neues Beispielprojekt mit einer solchen zusammensetzbaren Funktion. Eine Beispiel-Benutzeroberfläche zum Anzeigen dieser Elemente könnte wie folgt 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. Versuchen Sie, dies zur Benutzeroberfläche Ihrer App hinzuzufügen, 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.
Mit diesen Änderungen an der Konfiguration Ihrer App können Sie eine schnelle Umstellung von den erwarteten Extremen mit einem Splitscreen auf einem kleinen Mobilgerät in den Vollbildmodus auf einem Tablet oder Desktop-Computer simulieren. So können Sie nicht nur das Layout Ihrer App auf verschiedenen Bildschirmen testen, sondern auch testen, wie gut Ihre App auf schnelle Konfigurationsänderungen reagieren kann.
4. Lebenszyklusereignisse von Aktivitäten protokollieren
Eine weitere Auswirkung auf die Größenanpassung des Fensters im freien Format für Ihre App sind die verschiedenen Änderungen des Activity
-Lebenszyklus, die für Ihre App vorgenommen werden. Wenn Sie diese Änderungen in Echtzeit sehen möchten, fügen Sie Ihrer onCreate
-Methode einen Lebenszyklusbeobachter hinzu und protokollieren Sie jedes neue Lebenszyklusereignis, 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 nun Ihre App noch einmal auf dem Testgerät aus und sehen Sie sich logcat an, um Ihre App zu minimieren und wieder in den Vordergrund zu bringen.
Achten Sie darauf, dass Ihre App pausiert wird, wenn sie minimiert ist, und wieder fortgesetzt wird, wenn sie in den Vordergrund gebracht wird. Das hat Auswirkungen auf Ihre App, die Sie im nächsten Abschnitt dieses Codelabs behandeln werden, der sich auf Kontinuität konzentriert.
Sehen Sie sich nun Logcat an, um zu sehen, welche Callbacks für den Aktivitätslebenszyklus aufgerufen werden, wenn Sie die Größe Ihrer App von der kleinstmöglichen Größe auf die größtmögliche Größe ändern.
Abhängig von Ihrem Testgerät können Sie verschiedene Verhaltensweisen beobachten. Wahrscheinlich haben Sie jedoch bemerkt, dass Ihre Aktivität zerstört und neu erstellt wird, wenn sich die Größe des Fensters Ihrer App erheblich ändert, jedoch nicht, wenn es sich nur geringfügig verändert. Das liegt daran, dass ab API 24 nur erhebliche Größenänderungen zur Neuerstellung von Activity
führen.
Sie haben nun einige der gängigen Konfigurationsänderungen kennengelernt, die Sie in einer Freiform-Windowing-Umgebung erwarten können. Es gibt jedoch noch weitere Änderungen, die Sie berücksichtigen sollten. Ist beispielsweise ein externer Monitor an das Testgerät angeschlossen, wird Activity
gelöscht und neu erstellt, um Konfigurationsänderungen wie die Anzeigedichte zu berücksichtigen.
Um einen Teil der mit den Konfigurationsänderungen verbundenen Komplexität zu abstrahieren, verwenden Sie zur Implementierung der adaptiven UI APIs auf höherer Ebene wie WindowSizeClass. Weitere Informationen finden Sie unter Unterstützung verschiedener Bildschirmgrößen.
5. Continuity – Zusammensetzbare Funktionen verwalten interner Zustand bei Größenanpassung
Im vorherigen Abschnitt haben Sie einige Konfigurationsänderungen kennengelernt, die Ihre App in einer Umgebung mit kostenloser Fenstergröße erwartet. In diesem Abschnitt behalten Sie den UI-Status Ihrer App während dieser Änderungen bei.
Erweitern Sie zuerst die zusammensetzbare Funktion NavigationDrawerHeader
(zu finden in ReplyHomeScreen.kt
), damit die E-Mail-Adresse angezeigt wird, wenn darauf geklickt 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)
),
)
}
}
}
Wenn Sie der App den maximierbaren Header hinzugefügt haben,
- App auf dem Testgerät ausführen
- Tippen Sie auf die Überschrift, um sie zu maximieren.
- versuchen Sie, die Größe des Fensters zu ändern
Sie werden feststellen, dass der Status des Headers verloren geht, wenn die Größe wesentlich geändert wird.
Der UI-Status geht verloren, da Sie mit remember
den Status bei Neuzusammensetzungen beibehalten können, jedoch nicht über Aktivitäten oder Neuerstellungen von Prozessen hinweg. Üblicherweise wird eine Zustandshebung verwendet, bei der der Zustand in den Aufrufer einer zusammensetzbaren Funktion verschoben wird, um die zusammensetzbaren Funktionen zustandslos zu machen. Dadurch kann dieses Problem vermieden werden. Sie können remember
jedoch an Stellen verwenden, an denen der Status des UI-Elements intern für zusammensetzbare Funktionen bleibt.
Ersetzen Sie remember
durch rememberSaveable
, um diese Probleme zu beheben. Dies funktioniert, weil rememberSaveable
den gespeicherten Wert speichert und in savedInstanceState
wiederherstellt. Ändere remember
zu rememberSaveable
, führe die App auf dem Testgerät aus und versuche noch einmal, die Größe der App anzupassen. Sie werden feststellen, dass der Status der maximierbaren Kopfzeile wie vorgesehen während der Größenänderung beibehalten wird.
6. Unnötige Duplizierung von Hintergrundarbeiten vermeiden
Sie haben erfahren, wie Sie mit rememberSaveable
zusammensetzbare Funktionen interner UI-Status durch Konfigurationsänderungen, die häufig aufgrund von Größenanpassungen im freien Format auftreten können. Eine App sollte jedoch häufig den UI-Zustand und die Logik von den zusammensetzbaren Funktionen wegziehen. Das Übertragen der Inhaberschaft eines Status auf ein ViewModel ist eine der besten Möglichkeiten, den Zustand während der Größenänderung beizubehalten. Wenn du deinen Status in einen ViewModel
ziehst, kann es zu Problemen mit lang andauernden Hintergrundarbeiten wie umfangreichem Dateisystemzugriff oder Netzwerkaufrufen kommen, die zur Initialisierung des Bildschirms erforderlich sind.
Um ein Beispiel für die Arten von Problemen zu sehen, auf die Sie stoßen könnten, fügen Sie der Methode initializeUIState
in ReplyViewModel
eine Loganweisung 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 nun die App auf Ihrem Testgerät aus und versuchen Sie mehrmals, die Größe des App-Fensters anzupassen.
Wenn Sie sich Logcat ansehen, werden Sie feststellen, dass in Ihrer App angezeigt wird, dass die Initialisierungsmethode mehrmals ausgeführt wurde. Dies kann bei Aufgaben zu Problemen führen, die Sie nur einmal zur Initialisierung Ihrer UI ausführen möchten. Zusätzliche Netzwerkaufrufe, Datei-E/A oder andere Aktionen können die Geräteleistung beeinträchtigen und andere unbeabsichtigte Probleme verursachen.
Entfernen Sie den initializeUIState()
-Aufruf aus der onCreate()
-Methode Ihrer Aktivität, um unnötige Hintergrundarbeiten zu vermeiden. Initialisieren Sie stattdessen die Daten in der init
-Methode von ViewModel
. Dadurch wird die Initialisierungsmethode nur einmal ausgeführt, wenn ReplyViewModel
zum ersten Mal instanziiert wird:
init {
initializeUIState()
}
Versuchen Sie, die App erneut auszuführen. Sie können die unnötige simulierte Initialisierungsaufgabe nur einmal ausführen, unabhängig davon, wie oft Sie die Größe des App-Fensters ändern. Das liegt daran, dass ViewModels über den Lebenszyklus von Activity
hinaus erhalten bleiben. Da der Initialisierungscode beim Erstellen von ViewModel
nur einmal ausgeführt wird, wird er von allen Activity
-Neuerstellungen getrennt und unnötige Vorgänge vermieden. Würde es sich tatsächlich um einen teuren Serveraufruf oder eine umfangreiche Datei-E/A-Initialisierung der Benutzeroberfläche handeln, würden Sie erhebliche Ressourcen sparen und die Nutzererfahrung verbessern.
7. Das wars auch schon!
Geschafft! Super! Sie haben nun einige Best Practices implementiert, damit die Größe von Android-Apps unter ChromeOS und anderen Mehrfenster- und Multiscreen-Umgebungen gut angepasst werden kann.
Beispielquellcode
Repository von GitHub klonen
git clone https://github.com/android/large-screen-codelabs/
...oder eine ZIP-Datei des Repositorys herunterladen und extrahieren