Auf dieser Seite wird beschrieben, wie Sie mit Glance Größen handhaben und flexible und responsive Layouts bereitstellen.
Box
, Column
und Row
verwenden
Glance hat drei zusammensetzbare Hauptlayouts:
Box
: Setzt Elemente übereinander. Er wird mitRelativeLayout
übersetzt.Column
: Platziert Elemente in der vertikalen Achse hintereinander. Es wird in einLinearLayout
-Objekt mit vertikaler Ausrichtung umgewandelt.Row
: Platziert Elemente in der horizontalen Achse hintereinander. Es wird in einLinearLayout
-Objekt mit horizontaler Ausrichtung umgewandelt.
Mit jeder dieser zusammensetzbaren Funktionen können Sie die vertikale und horizontale Ausrichtung des Inhalts sowie die Einschränkungen für Breite, Höhe, Gewicht und Abstände mithilfe von Modifizierern definieren. Darüber hinaus kann jedes untergeordnete Element seinen Modifikator definieren, um den Abstand und die Platzierung im übergeordneten Element zu ändern.
Das folgende Beispiel zeigt, wie Sie einen Row
erstellen, der seine untergeordneten Elemente gleichmäßig horizontal verteilt, wie in Abbildung 1 dargestellt:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Der Row
füllt die maximal verfügbare Breite aus. Da alle untergeordneten Elemente dieselbe Gewichtung haben, teilen sie sich den verfügbaren Platz gleichmäßig auf. Sie können verschiedene Stärken, Größen, Abstände oder Ausrichtungen definieren, um Layouts an Ihre Anforderungen anzupassen.
Scrollbare Layouts verwenden
Eine weitere Möglichkeit, responsive Inhalte bereitzustellen, besteht darin, sie scrollbar zu machen. Mit der zusammensetzbaren Funktion LazyColumn
ist dies möglich. Mit dieser zusammensetzbaren Funktion können Sie eine Reihe von Elementen definieren, die in einem scrollbaren Container im App-Widget angezeigt werden sollen.
Die folgenden Snippets zeigen verschiedene Möglichkeiten zum Definieren von Elementen in LazyColumn
.
Sie können die Anzahl der Elemente angeben:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Einzelne Elemente bereitstellen:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Geben Sie eine Liste oder ein Array mit Elementen an:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
Sie können auch eine Kombination der vorherigen Beispiele verwenden:
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
itemId
ist im vorherigen Snippet nicht angegeben. Durch Angabe von itemId
wird die Leistung verbessert und die Scrollposition durch die Liste und die appWidget
-Updates ab Android 12 beibehalten (z. B. beim Hinzufügen oder Entfernen von Elementen in der Liste). Das folgende Beispiel zeigt, wie ein itemId
angegeben wird:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
SizeMode
definieren
Die Größen von AppWidget
können sich je nach Gerät, Auswahl des Nutzers oder Launcher unterscheiden. Daher ist es wichtig, flexible Layouts wie auf der Seite Flexible Widget-Layouts bereitstellen beschrieben bereitzustellen. Glance vereinfacht dies mit der SizeMode
-Definition und dem LocalSize
-Wert. In den folgenden Abschnitten werden die drei Modi beschrieben.
SizeMode.Single
SizeMode.Single
ist der Standardmodus. Damit wird angegeben, dass nur ein Inhaltstyp angegeben ist. Das heißt, selbst wenn sich die verfügbare Größe für AppWidget
ändert, ändert sich die Größe des Inhalts nicht.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
Stellen Sie bei der Verwendung dieses Modus Folgendes sicher:
- Die Metadatenwerte mit Mindest- und Höchstgröße werden anhand der Inhaltsgröße korrekt definiert.
- Der Inhalt ist innerhalb des erwarteten Größenbereichs flexibel genug.
Sie sollten diesen Modus in der Regel in folgenden Fällen verwenden:
a) das AppWidget
eine feste Größe hat oder
b) es bei der Größenanpassung seinen Inhalt nicht ändert.
SizeMode.Responsive
Dieser Modus entspricht der Bereitstellung von responsiven Layouts, bei denen mit dem GlanceAppWidget
eine Reihe von responsiven Layouts definiert werden kann, die durch bestimmte Größen begrenzt sind. Für jede definierte Größe wird der Inhalt erstellt und der jeweiligen Größe zugeordnet, wenn das AppWidget
erstellt oder aktualisiert wird. Das System wählt dann anhand der verfügbaren Größe die passendste aus.
In unserem Ziel AppWidget
können Sie beispielsweise drei Größen und deren Inhalt definieren:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
Im vorherigen Beispiel wird die Methode provideContent
dreimal aufgerufen und der definierten Größe zugeordnet.
- Im ersten Aufruf wird als Größe
100x100
ausgewertet. Zu den Inhalten gehören weder die zusätzliche Schaltfläche noch der obere und untere Text. - Im zweiten Aufruf wird als Größe
250x100
ausgewertet. Der Inhalt enthält die zusätzliche Schaltfläche, aber nicht den oberen und unteren Text. - Im dritten Aufruf wird die Größe
250x250
ausgewertet. Der Inhalt enthält die zusätzliche Schaltfläche und beide Texte.
SizeMode.Responsive
ist eine Kombination der beiden anderen Modi. Damit können Sie responsive Inhalte innerhalb vordefinierter Grenzen definieren. Dieser Modus ist in der Regel besser und ermöglicht flüssigere Übergänge, wenn die Größe von AppWidget
angepasst wird.
Die folgende Tabelle zeigt den Wert der Größe abhängig von den verfügbaren Größen für SizeMode
und AppWidget
:
Verfügbare Größe | 105 × 110 | 203 x 112 | 72 x 72 | 203 x 150 |
---|---|---|---|---|
SizeMode.Single |
110 x 110 | 110 x 110 | 110 x 110 | 110 x 110 |
SizeMode.Exact |
105 × 110 | 203 x 112 | 72 x 72 | 203 x 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* Die genauen Werte dienen nur zu Demozwecken. |
SizeMode.Exact
SizeMode.Exact
entspricht der Bereitstellung exakter Layouts, bei denen der GlanceAppWidget
-Inhalt jedes Mal angefordert wird, wenn sich die verfügbare AppWidget
-Größe ändert (z. B. wenn der Nutzer die Größe von AppWidget
auf dem Startbildschirm ändert).
Im Ziel-Widget kann beispielsweise eine zusätzliche Schaltfläche hinzugefügt werden, wenn die verfügbare Breite größer als ein bestimmter Wert ist.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
Dieser Modus bietet mehr Flexibilität als die anderen, birgt jedoch auch einige Nachteile:
- Der
AppWidget
muss bei jeder Größenänderung vollständig neu erstellt werden. Dies kann bei komplexen Inhalten zu Leistungsproblemen und Sprüngen in der Benutzeroberfläche führen. - Die verfügbare Größe kann je nach Implementierung des Launchers variieren. Wenn im Launcher beispielsweise keine Liste der Größen enthalten ist, wird die geringstmögliche Größe verwendet.
- Auf Geräten, die älter als Android 12 sind, funktioniert die Logik zur Größenberechnung möglicherweise nicht in allen Situationen.
Sie sollten diesen Modus im Allgemeinen verwenden, wenn SizeMode.Responsive
nicht verwendet werden kann (d. h. eine kleine Gruppe responsiver Layouts nicht möglich ist).
Auf Ressourcen zugreifen
Verwenden Sie LocalContext.current
, um auf eine Android-Ressource zuzugreifen, wie im folgenden Beispiel gezeigt:
LocalContext.current.getString(R.string.glance_title)
Wir empfehlen, Ressourcen-IDs direkt anzugeben, um die Größe des endgültigen RemoteViews
-Objekts zu verringern und dynamische Ressourcen wie dynamische Farben zu aktivieren.
Zusammensetzbare Funktionen und Methoden akzeptieren Ressourcen, die einen „Anbieter“ wie ImageProvider
oder eine Überlastmethode wie GlanceModifier.background(R.color.blue)
verwenden. Beispiele:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Verschiedene Schaltflächen hinzufügen
Zusammenfassende Schaltflächen wurden in Android 12 eingeführt. Glance unterstützt die Abwärtskompatibilität für die folgenden Arten von zusammengesetzten Schaltflächen:
Diese zusammengesetzten Schaltflächen zeigen jeweils eine anklickbare Ansicht für den Status "Aktiviert" an.
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
Wenn sich der Status ändert, wird das angegebene Lambda ausgelöst. Sie können den Prüfstatus wie im folgenden Beispiel speichern:
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
Du kannst das Attribut colors
auch für CheckBox
, Switch
und RadioButton
angeben, um deren Farben anzupassen:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )