本页将介绍如何处理尺寸,以及如何提供灵活且自适应的 和 Glance 布局,并使用现有的 Glance 组件。
使用 Box
、Column
和 Row
Glance 有三种主要的可组合项布局:
Box
:将元素叠放在另一个元素上。它会转换为RelativeLayout
。Column
:在纵轴上将元素一个接一个地放置。它可以将 转换为垂直方向的LinearLayout
。Row
:在横轴上将元素一个接一个地放置。它可以将 设置为水平方向的LinearLayout
。
Glance 支持 Scaffold
对象。放置您的Column
、Row
和
给定 Scaffold
对象中的 Box
可组合项。
每个可组合项都允许您定义垂直和水平对齐方式 宽度、高度、重量或内边距约束条件, 修饰符。此外,每个子项都可以定义其修饰符,以更改 和在父级内的放置位置
以下示例展示了如何创建均匀分布的 Row
水平方向,如图 1 所示:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
填充了最大可用宽度,因为每个子项具有相同的宽度
它们便会均分可用空间。您可以定义不同的权重、
尺寸、内边距或对齐方式,根据您的需求调整布局。
使用可滚动布局
提供自适应内容的另一种方法是使其可滚动。这是
LazyColumn
可组合项。借助此可组合项,您可以定义一组
要在应用 widget 中的可滚动容器内显示的列表项的比例。
以下代码段展示了在
LazyColumn
。
您可以提供商品数量:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
请提供各项内容:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
提供项列表或数组:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
您也可以组合使用上述示例:
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
。指定
itemId
有助于提高性能并保持滚动
从 Android 12 及更高版本通过列表和 appWidget
更新来确定位置(适用于
(例如,在列表中添加或移除项目时)。以下示例
展示了如何指定 itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
定义 SizeMode
AppWidget
大小可能会因设备、用户选择或启动器而异。
因此请务必提供灵活的布局,如提供
灵活的 widget 布局页面。Glance 通过 SizeMode
简化这一过程
定义和 LocalSize
值。以下部分介绍了
模式。
SizeMode.Single
SizeMode.Single
是默认模式。它表示只有一种类型的
提供的内容;也就是说,即使 AppWidget
的可用大小发生变化,
内容大小不会改变
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 // ... } }
使用此模式时,请确保:
- 最小和最大大小元数据值根据 调整内容大小
- 内容在预期尺寸范围内足够灵活。
通常,如果出现以下任一情况,您应使用此模式:
a) AppWidget
具有固定大小,或
b) 在调整大小时不会更改其内容。
SizeMode.Responsive
此模式相当于提供自适应布局,让您可以
GlanceAppWidget
,用于定义一组受特定布局约束的自适应布局
尺寸。对于每个指定的尺寸,系统都会创建内容并将其映射到特定的
创建或更新 AppWidget
时的大小。然后,系统会选择
最适合的图片尺寸。
例如,在我们的目的地 AppWidget
中,您可以定义三种尺寸及其
内容:
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") } } } }
在前面的示例中,provideContent
方法被调用了三次,
映射到指定的尺寸
- 在第一次调用中,大小的计算结果为
100x100
。内容不符合 添加额外的按钮,以及顶部和底部的文字。 - 在第二个调用中,大小的计算结果为
250x100
。内容包括 而不是顶部和底部的文字。 - 在第三个调用中,大小的计算结果为
250x250
。内容包括 额外的按钮和两个文本。
SizeMode.Responsive
是其他两种模式的组合,可让您
在预定义的边界内定义自适应内容。一般情况下,此模式
性能更好,并且在调整 AppWidget
大小时允许更平滑的过渡。
下表显示了根据 SizeMode
和
AppWidget
的可用大小:
可用尺寸 | 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 |
* 确切值仅用于演示目的。 |
SizeMode.Exact
SizeMode.Exact
相当于提供确切布局,
每当可用的 AppWidget
大小时,请求 GlanceAppWidget
内容
更改(例如,当用户在主屏幕上调整 AppWidget
的大小时)。
例如,在目的地 widget 中,如果 可用宽度大于特定值。
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") } } } } }
此模式比其他模式提供更高的灵活性, 注意事项:
- 每当大小发生变化时,都必须完全重新创建
AppWidget
。这个 当内容复杂时,可能会导致性能问题和界面跳跃。 - 可用大小可能会因启动器的实现而异。 例如,如果启动器未提供尺寸列表, 使用可能的尺寸
- 在搭载 Android 12 之前版本的设备上,大小计算逻辑可能不适用于部分设备 情况。
通常,如果无法使用 SizeMode.Responsive
,则应使用此模式
(也就是说,使用一小部分自适应布局是不可行的)。
访问资源
使用 LocalContext.current
访问任何 Android 资源,如
示例:
LocalContext.current.getString(R.string.glance_title)
我们建议您直接提供资源 ID,以缩减
RemoteViews
对象,并启用动态资源,例如动态
颜色。
可组合项和方法使用“提供程序”接受资源,例如
ImageProvider
,或者使用诸如以下的重载方法:
GlanceModifier.background(R.color.blue)
。例如:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
标识名文本
Glance 1.1.0 包含一个用于设置文本样式的 API。使用以下内容设置文字样式:
TextStyle 类的 fontSize
、fontWeight
或 fontFamily
属性,
fontFamily
支持所有系统字体(如下例所示),但
不支持在应用中使用自定义字体:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
添加复合按钮
复合按钮是在 Android 12 中引入的。Glance 支持向后显示 兼容以下类型的复合按钮:
每个复合按钮都会显示一个可点击的视图,表示 “已选中”状态。
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" )
当状态发生变化时,会触发所提供的 lambda。您可以将 检查状态,如以下示例所示:
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) } ) } }
您还可以向 CheckBox
、Switch
和colors
RadioButton
自定义自己的颜色:
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) ), )
其他组件
Glance 1.1.0 发布了其他组件,如 下表:
名称 | 图片 | 参考链接 | 其他说明 |
---|---|---|---|
实心按钮 | 组件 | ||
轮廓按钮 | 组件 | ||
图标按钮 | 组件 | 主要 / 次要 / 仅图标 | |
标题栏 | 组件 | ||
Scaffold | Scaffold 和标题栏位于同一演示中。 |
有关设计细节的详细信息,请参见 设计套件。