提问人:Daniel Hernández 提问时间:11/18/2023 最后编辑:Daniel Hernández 更新时间:11/22/2023 访问量:171
Jetpack Compose 主导航中的全屏 IconList
Full-Screen IconList in Jetpack Compose Main Navigation
问:
考虑到我尝试使用 LazyVerticalGrid.modifier.fillMaxHeight() 和 fillMaxSize() 等修饰符而没有达到预期的结果,如何确保在 Scaffold 组件中使用时 Jetpack Compose Main 导航视图中的 IconList 占据整个屏幕?
编辑:
根据@EthanWu建议对我的代码进行了一些更改,密度不考虑使用顶栏应用的填充
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Preview
@Composable
fun HomeScreenPrev() {
TotalApplicationTheme {
val functionalities = listOf(
Functionality("Search", "Search functionality", Icons.Default.ShoppingCart),
Functionality("Settings", "Settings functionality", Icons.Filled.Face),
Functionality("Like", "Like functionality", Icons.Default.ThumbUp),
Functionality("Warning", "Warning functionality", Icons.Default.Warning),
Functionality("Search", "Search functionality", Icons.Default.Search),
Functionality("Settings", "Settings functionality", Icons.Default.Settings),
)
HomeScreen(functionalities = functionalities)
}
}
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun HomeScreen(functionalities: List<Functionality>) {
Scaffold(
topBar = { MainTopBar() },
) { ActionPanel(functionalities = functionalities, modifier = Modifier.padding(it)) }
}
@Composable
fun ActionPanel(functionalities: List<Functionality>, modifier: Modifier) {
val density = LocalDensity.current
with(density) {
val eachHeight =
(LocalConfiguration.current.screenHeightDp) * density.density / (functionalities.size / 2)
LazyVerticalGrid(
modifier = modifier, columns = GridCells.Fixed(2)
) {
items(functionalities) {
TaskCard(it, modifier = Modifier.height(eachHeight.toDp()))
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskCard(functionality: Functionality, modifier: Modifier = Modifier) {
Card(modifier = modifier,
shape = RectangleShape,
border = BorderStroke(1.dp, Color.LightGray),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.tertiary, //Card background color
contentColor = Color.Black //Card content color,e.g.text
),
onClick = {
/*TODO*/
}) {
Column(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Spacer(modifier = Modifier.height(8.dp))
Icon(
imageVector = functionality.icon,
contentDescription = null,
modifier = Modifier
.size(48.dp)
.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(8.dp))
Text(text = functionality.name, style = MaterialTheme.typography.titleSmall)
Spacer(modifier = Modifier.height(4.dp))
Text(text = functionality.description, style = MaterialTheme.typography.bodyMedium)
}
}
}
当前结果
答:
1赞
EthanWu
11/20/2023
#1
您可以使用“布局检查器”来了解布局的外观。
Android 开发人员的本指南会有所帮助。
但是,您可能会发现应该设置网格中每行的高度以达到您想要执行的操作。
为此,您可以尝试获取当前布局高度并将其除以列数,然后将每个项目的高度设置为该值。
val density = LocalDensity.current
with(density){
val h = LocalConfiguration.current.screenHeightDp*density.density/(listItem.size/numberOfColumns)
LazyVerticalGrid(
...
){
items(...){
Box(
modifier = Modifier
.fillMaxWidth()
.height(h.toDp())
){
...
}
}
}
}
结果是这样的:
编辑:
下面是一个示例:
val density = LocalDensity.current
Test2Theme {
// A surface container using the 'background' color from the theme
Scaffold(modifier = Modifier.fillMaxSize()) {
val listItem = listOf("1", "2", "3", "4", "5", "6")
val numberOfColumns = 2
with(density) {
val eachHeight =
LocalConfiguration.current.screenHeightDp*density.density/(listItem.size/numberOfColumns)
LazyVerticalGrid(
columns = GridCells.Fixed(numberOfColumns),
modifier = Modifier
.padding(it)
.fillMaxSize()
) {
items(listItem) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(eachHeight.toDp())
.border(
BorderStroke(1.dp, Color.Black)
)
) {
Text(
text = it,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxSize()
)
}
}
}
}
}
}
不同分辨率的结果
编辑:
以上方式仅适用于全屏。 如果要获取组件大小,Modifier.onGloballyPosition 会在合成后传递大小。
因此,要实现您的目标,就像:
val listItem = listOf("1", "2", "3", "4", "5", "6")
val numberOfColumns = 2
var eachHeight by remember {
mutableStateOf(0.dp)
}
LazyVerticalGrid(
columns = GridCells.Fixed(numberOfColumns),
modifier = Modifier
.padding(it)
.fillMaxSize()
.onGloballyPositioned {
eachHeight =
with(density) { (it.size.height / (listItem.size / numberOfColumns)).toDp() }
}
) {
items(listItem) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(eachHeight)
.border(
BorderStroke(1.dp, Color.Black)
)
) {
...
}
}
}
结果是这样的:
或者您可以创建自定义布局,例如
@Composable
fun MyGridLayout(
modifier: Modifier = Modifier,
row: Int,
col: Int,
content: @Composable ()-> Unit,
){
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
//calculate each children's width & height
var placeable = measurables.map {
val width = constraints.maxWidth/col
val height = constraints.maxHeight/row
var c = Constraints(minWidth = width, maxWidth = width, maxHeight = height, minHeight = height)
it.measure(c)
}
//then place them
layout(constraints.maxWidth, constraints.maxHeight) {
var yPosition = 0
var xPosition = 0
var mCol = 1
placeable.forEach {
it.placeRelative(xPosition, yPosition)
if (mCol % col != 0) {
xPosition += it.width
}
else {
xPosition = 0
yPosition += it.height
}
mCol+=1
}
}
}
并像这样使用它
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
...
)
}
) {
val listItem = listOf("1", "2", "3", "4", "5", "6")
val numberOfColumns = 2
MyGridLayout(
modifier = Modifier.padding(it).fillMaxSize(),
row = listItem.size/numberOfColumns,
col = numberOfColumns
){
listItem.forEach { str->
Box(
modifier = Modifier
.border(
BorderStroke(1.dp, Color.Black)
)
) {
...
}
}
}
}
两个结果是一样的。
评论
0赞
Daniel Hernández
11/21/2023
我不完全理解使用列数来计算每个单元格高度背后的逻辑,我试图实现您在此处显示的内容,但仍然有空白
0赞
EthanWu
11/21/2023
对不起这个错误,我已经更新了答案,“numberOfColumn”应该是每行的列数,在这种情况下,它应该是 2。
0赞
Daniel Hernández
11/22/2023
它在某种程度上有效,现在的问题是密度没有考虑到 TopBar 的空间
0赞
EthanWu
11/22/2023
LocalConfiguration.current.screenHeightDp
检索设备屏幕的高度。如果要获取组件大小,则该尺寸应无法正常工作。
0赞
Daniel Hernández
11/23/2023
自定义 GridLayout 完美运行
评论