Auf dieser Seite wird beschrieben, wie Sie mit Glance Größen verarbeiten und flexible und responsive Layouts mit vorhandenen Glance-Komponenten bereitstellen.
Box, Column und Row verwenden
Glance bietet drei Hauptlayouts, die zusammengesetzt werden können:
Box: Platziert Elemente übereinander. Entspricht einemRelativeLayout.Column: Platziert Elemente untereinander auf der vertikalen Achse. Entspricht einemLinearLayoutmit vertikaler Ausrichtung.Row: Platziert Elemente nebeneinander auf der horizontalen Achse. Entspricht einemLinearLayoutmit horizontaler Ausrichtung.
Glance unterstützt Scaffold-Objekte. Platzieren Sie die zusammensetzbaren Elemente Column, Row und Box in einem bestimmten Scaffold-Objekt.
Mit jedem dieser zusammensetzbaren Elemente können Sie die vertikale und horizontale Ausrichtung des Inhalts sowie die Einschränkungen für Breite, Höhe, Gewicht oder Padding mithilfe von Modifikatoren definieren. Außerdem kann jedes untergeordnete Element seinen Modifikator definieren, um den Abstand und die Platzierung innerhalb des übergeordneten Elements zu ändern.
Im folgenden Beispiel wird gezeigt, wie Sie eine Row erstellen, in der die untergeordneten Elemente horizontal gleichmäßig verteilt sind, wie in Abbildung 1 zu sehen:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Die Row füllt die maximal verfügbare Breite aus. Da alle untergeordneten Elemente dasselbe Gewicht haben, teilen sie sich den verfügbaren Platz gleichmäßig. Sie können verschiedene Gewichte, Größen, Paddings 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. Dies ist mit dem zusammensetzbaren Element LazyColumn möglich. Mit diesem zusammensetzbaren Element 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, Elemente in der LazyColumn zu definieren.
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 angeben:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Eine Liste oder ein Array von Elementen angeben:
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") } }
Beachten Sie, dass im vorherigen Snippet die itemId nicht angegeben ist. Wenn Sie die itemId angeben, können Sie die Leistung verbessern und die Scrollposition bei Listen- und appWidget-Updates ab Android 12 beibehalten (z. B. beim Hinzufügen oder Entfernen von Elementen aus der Liste). Im folgenden Beispiel wird gezeigt, wie Sie eine itemId angeben:
items(items = peopleList, itemId = { person -> person.id.hashCode().toLong() }) { person -> Text(person.name) }
Snap Scrolling
Snap Scrolling ist eine Animation, mit der scrollbare Inhalte an den oberen Rand des Widget-Containers gescrollt werden.
Damit Sie Snap Scrolling implementieren können, müssen die folgenden Bedingungen erfüllt sein:
- Aktualisieren Sie die Glance-Abhängigkeit auf Version 1.3.0-alpha02 oder höher.
- Legen Sie
compileSdkauf 37 oder höher fest, da Snap Scrolling auf Geräten mit Android 17 und höher unterstützt wird. - Konfigurieren Sie die
LazyColumnmitVerticalScrollMode. Wenn das Gerät Snap Scrolling unterstützt, verwenden SieSnapScrollMatchHeight. Andernfalls verwenden SieNormal.
Wenn Sie Snap Scrolling mit Bildern verwenden, sehen Sie sich das kanonische Layout für Bilder ohne Rand an.
@Composable fun SnapScrollLayout() { val height = LocalSize.current.height val items = listOf( ColorItem(Color.Red, "Red"), ColorItem(Color.Yellow, "Yellow"), ColorItem(Color.Blue, "Blue") ) if (Build.VERSION.SDK_INT >= 36 && isSnapScrollSupported) { LazyColumn( verticalScrollMode = VerticalScrollMode.SnapScrollMatchHeight(height) ) { items(items) { item -> ColorCard(item, height) } } } else { LazyColumn { items(items) { item -> ColorCard(item, height) } } } } @Composable private fun ColorCard(item: ColorItem, height: Dp) { Box( modifier = GlanceModifier .background(item.color) .fillMaxWidth() .height(height), contentAlignment = Alignment.Center ) { Text( text = item.name, modifier = GlanceModifier.background(Color.White) ) } } val isSnapScrollSupported: Boolean get() = Build.VERSION.SDK_INT >= 36 && Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1
SizeMode definieren
AppWidget-Größen können je nach Gerät, Nutzerauswahl oder Launcher variieren,
daher ist es wichtig, flexible Layouts bereitzustellen, wie auf der Seite Flexible Widget-Layouts bereitstellen beschrieben. Glance vereinfacht dies mit der Definition SizeMode und dem Wert LocalSize. In den folgenden Abschnitten werden die drei Modi beschrieben.
SizeMode.Single
SizeMode.Single ist der Standardmodus. Er gibt an, dass nur ein Inhaltstyp bereitgestellt wird. Das heißt, auch wenn sich die verfügbare Größe des 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 // ... } }
Wenn Sie diesen Modus verwenden, beachten Sie Folgendes:
- Die Metadatenwerte für die Mindest- und Höchstgröße sind basierend auf der Inhaltsgröße richtig definiert.
- Der Inhalt ist im erwarteten Größenbereich flexibel genug.
Im Allgemeinen sollten Sie diesen Modus verwenden, wenn:
a) das AppWidget eine feste Größe hat oder b) sich der Inhalt beim Ändern der Größe nicht ändert.
SizeMode.Responsive
Dieser Modus entspricht der Bereitstellung responsiver Layouts, mit denen
das GlanceAppWidget eine Reihe responsiver Layouts definieren 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 basierend auf der verfügbaren Größe das am besten passende Layout 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.
- Beim ersten Aufruf wird die Größe auf
100x100festgelegt. Der Inhalt enthält weder die zusätzliche Schaltfläche noch die Texte oben und unten. - Beim zweiten Aufruf wird die Größe auf
250x100festgelegt. Der Inhalt enthält die zusätzliche Schaltfläche, aber nicht die Texte oben und unten. - Beim dritten Aufruf wird die Größe auf
250x250festgelegt. Der Inhalt enthält die zusätzliche Schaltfläche und beide Texte.
SizeMode.Responsive ist eine Kombination der beiden anderen Modi und ermöglicht es Ihnen, responsive Inhalte innerhalb vordefinierter Grenzen zu definieren. Im Allgemeinen bietet dieser Modus eine bessere Leistung und ermöglicht reibungslosere Übergänge, wenn die Größe des AppWidget geändert wird.
In der folgenden Tabelle ist der Wert der Größe je nach SizeMode und verfügbarer Größe des AppWidget aufgeführt:
| Verfügbare Größe | 105 x 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 x 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 Inhalt des GlanceAppWidget jedes Mal angefordert wird, wenn sich die verfügbare Größe des AppWidget ändert (z. B. wenn der Nutzer die Größe des AppWidget auf dem Startbildschirm ändert).
Im Ziel-Widget kann beispielsweise eine zusätzliche Schaltfläche hinzugefügt werden, wenn die verfügbare Breite einen bestimmten Wert überschreitet.
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, hat aber auch einige Nachteile:
- Das
AppWidgetmuss jedes Mal neu erstellt werden, wenn sich die Größe ändert. Bei komplexen Inhalten kann dies zu Leistungsproblemen und UI-Sprüngen führen. - Die verfügbare Größe kann je nach Implementierung des Launchers variieren. Wenn der Launcher beispielsweise keine Liste der Größen bereitstellt, wird die kleinstmögliche Größe verwendet.
- Auf Geräten vor Android 12 funktioniert die Logik zur Größenberechnung möglicherweise nicht in allen Situationen.
Im Allgemeinen sollten Sie diesen Modus verwenden, wenn SizeMode.Responsive nicht verwendet werden kann (d. h. eine kleine Anzahl responsiver Layouts nicht möglich ist).
Auf Ressourcen zugreifen
Verwenden Sie LocalContext.current, um auf eine beliebige 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 reduzieren und dynamische Ressourcen wie dynamische
Farben zu aktivieren.
Zusammensetzbare Elemente und Methoden akzeptieren Ressourcen über einen „Provider“ wie ImageProvider oder über eine Überladungsmethode wie GlanceModifier.background(R.color.blue). Beispiel:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Text verarbeiten
Glance 1.1.0 enthält eine API zum Festlegen von Textstilen. Legen Sie Textstile mit den Attributen fontSize, fontWeight oder fontFamily der Klasse TextStyle fest.
fontFamily unterstützt alle Systemschriften, wie im folgenden Beispiel gezeigt. Benutzerdefinierte Schriften in Apps werden jedoch nicht unterstützt:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Zusammengesetzte Schaltflächen hinzufügen
Zusammengesetzte 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 an, die den Status „checked“ (ausgewählt) darstellt.
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableIntStateOf(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 die bereitgestellte Lambda-Funktion ausgelöst. Sie können den Status „checked“ speichern, wie im folgenden Beispiel gezeigt:
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) } ) } }
Sie können das Attribut colors auch für CheckBox, Switch, und
RadioButton angeben, um die 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) ), )
Zusätzliche Komponenten
Glance 1.1.0 enthält zusätzliche Komponenten, wie in der folgenden Tabelle beschrieben:
| Name | Bild | Referenzlink | Zusätzliche Hinweise |
|---|---|---|---|
| Gefüllter Button |
|
Komponente | |
| Schaltflächen mit Umriss |
|
Komponente | |
| Symbolschaltflächen |
|
Komponente | Primär / Sekundär / Nur Symbol |
| Titelleiste |
|
Komponente | |
| Gerüst | Gerüst und Titelleiste sind in derselben Demo. |
Weitere Informationen zu den Designspezifikationen finden Sie in diesem Design-Kit auf Figma.
Weitere Informationen zu kanonischen Layouts finden Sie unter Kanonische Widget-Layouts.