[英]Get height of element Jetpack Compose
我想在 Jetpack Compose 中獲取按鈕(或其他元素)的高度。 你知道怎么得到嗎?
如果您想在合成后獲得按鈕的高度,則可以使用: onGloballyPositionedModifier 。
它返回一個 LayoutCoordinates object,其中包含按鈕的大小。
一個完整的解決方案如下:
@Composable
fun GetHeightCompose() {
// get local density from composable
val localDensity = LocalDensity.current
var heightIs by remember {
mutableStateOf(0.dp)
}
Box(modifier = Modifier.fillMaxSize()) {
// Important column should not be inside a Surface in order to be measured correctly
Column(
Modifier
.onGloballyPositioned { coordinates ->
heightIs = with(localDensity) { coordinates.size.height.toDp() }
}) {
Text(text = "If you want to know the height of this column with text and button in Dp it is: $heightIs")
Button(onClick = { /*TODO*/ }) {
Text(text = "Random Button")
}
}
}
}
您還可以使用BoxWithConstraints
,如下所示:
Button(onClick = {}){
BoxWithConstraints{
val height = maxHeight
}
}
但我不確定它是否適合您的特定用例。
將Modifier.onSizeChanged{}
或Modifier.globallyPositioned{}
與 remember 和 mutableStateOf 一起使用需要另一個組合,並且根據您對這些值所做的操作,它可能會導致值跳轉。
例如
Column {
var sizeInDp by remember { mutableStateOf(DpSize.Zero) }
val density = LocalDensity.current
// In practice we don't know this size, it's for demonstration
Box(modifier = Modifier
.onSizeChanged {
sizeInDp = density.run {
DpSize(
it.width.toDp(),
it.height.toDp()
)
}
}
.size(200.dp)
.background(Color.Red))
Text(
"Hello World",
modifier = Modifier
.background(Color.White)
.size(sizeInDp)
)
}
文本的背景將從初始背景移動到下一次重組時的 200.dp 大小。 如果您正在執行更改任何 UI 繪圖的操作,用戶可能會注意到它是 flash 或故障。
在不重新組合的情況下獲取元素高度的第一個替代方法是使用BoxWithConstraints
BoxWithConstraints
' BoxScope
包含 dp 中的maxHeight
和 Int 中的constraints.maxHeigh
t。
但是 BoxWithConstraints 在某些條件下(例如使用 Modifier.fillMaxHeight、沒有任何大小修飾符或具有垂直滾動的父級返回不正確的值)返回的約束不是精確的大小
您可以查看有關從 BoxWithConstraints 返回的尺寸的答案,Constraints 部分顯示了使用 BoxWithConstraints 將獲得什么。
verticalScroll 返回 Constraints.Infinity 作為高度。
獲得精確尺寸的可靠方法是使用 SubcomposeLayout
**
* SubcomposeLayout that [SubcomposeMeasureScope.subcompose]s [mainContent]
* and gets total size of [mainContent] and passes this size to [dependentContent].
* This layout passes exact size of content unlike
* BoxWithConstraints which returns [Constraints] that doesn't match Composable dimensions under
* some circumstances
*
* @param placeMainContent when set to true places main content. Set this flag to false
* when dimensions of content is required for inside [mainContent]. Just measure it then pass
* its dimensions to any child composable
*
* @param mainContent Composable is used for calculating size and pass it
* to Composables that depend on it
*
* @param dependentContent Composable requires dimensions of [mainContent] to set its size.
* One example for this is overlay over Composable that should match [mainContent] size.
*
*/
@Composable
fun DimensionSubcomposeLayout(
modifier: Modifier = Modifier,
placeMainContent: Boolean = true,
mainContent: @Composable () -> Unit,
dependentContent: @Composable (Size) -> Unit
) {
SubcomposeLayout(
modifier = modifier
) { constraints: Constraints ->
// Subcompose(compose only a section) main content and get Placeable
val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
.map {
it.measure(constraints.copy(minWidth = 0, minHeight = 0))
}
// Get max width and height of main component
var maxWidth = 0
var maxHeight = 0
mainPlaceables.forEach { placeable: Placeable ->
maxWidth += placeable.width
maxHeight = placeable.height
}
val dependentPlaceables: List<Placeable> = subcompose(SlotsEnum.Dependent) {
dependentContent(Size(maxWidth.toFloat(), maxHeight.toFloat()))
}
.map { measurable: Measurable ->
measurable.measure(constraints)
}
layout(maxWidth, maxHeight) {
if (placeMainContent) {
mainPlaceables.forEach { placeable: Placeable ->
placeable.placeRelative(0, 0)
}
}
dependentPlaceables.forEach { placeable: Placeable ->
placeable.placeRelative(0, 0)
}
}
}
}
enum class SlotsEnum { Main, Dependent }
用法
val content = @Composable {
Box(
modifier = Modifier
.size(200.dp)
.background(Color.Red)
)
}
val density = LocalDensity.current
DimensionSubcomposeLayout(
mainContent = { content() },
dependentContent = { size: Size ->
content()
val dpSize = density.run {size.toDpSize() }
Box(Modifier.size(dpSize).border(3.dp, Color.Green))
},
placeMainContent = false
)
或者
DimensionSubcomposeLayout(
mainContent = { content() },
dependentContent = { size: Size ->
val dpSize = density.run {size.toDpSize() }
Box(Modifier.size(dpSize).border(3.dp, Color.Green))
}
)
在使用Modifier.onSizeChanged
時,我設法實施了一種變通方法,通過使從中讀取的可組合對象成為無狀態的,從而減少(不是完全移除)重組次數。 不是可用的最佳方法,但它可以完成工作。
@Composable
fun TextToMeasure(
currentHeight: Dp,
onHeightChanged: (Dp) -> Unit,
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
Text(
text = "Hello Android",
modifier = modifier
.onSizeChanged { size ->
val newHeight = with(density) { size.height.toDp }
if (newHeight != currentHeight) {
onHeightChanged(newHeight)
}
},
)
}
@Composable
fun MainScreen() {
val height = remember { mutableStateOf(0.dp) }
TextToMeasure(
currentHeight = height.value,
onHeightChanged = { height.value = it },
)
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.