android 获取元素Jetpack组合的高度

bn31dyow  于 2022-12-21  发布在  Android
关注(0)|答案(5)|浏览(332)

我想在Jetpack合成中得到我的按钮(或其他元素)的高度。你知道如何得到吗?

r6vfmomb

r6vfmomb1#

如果您想在合成后获得按钮的高度,则可以使用:onGloballyPositionedModifier.
它返回一个LayoutCoordinates对象,该对象包含按钮的大小。
使用onGloballyPositioned修饰符的示例:

@Composable
fun OnGloballyPositionedExample() {
    // Get local density from composable
    val localDensity = LocalDensity.current
    
    // Create element height in pixel state
    var columnHeightPx by remember {
        mutableStateOf(0f)
    }

    // Create element height in dp state
    var columnHeightDp by remember {
        mutableStateOf(0.dp)
    }

    Column(
        modifier = Modifier
            .onGloballyPositioned { coordinates ->
                // Set column height using the LayoutCoordinates
                columnHeightPx = coordinates.size.height.toFloat()
                columnHeightDp = with(localDensity) { coordinates.size.height.toDp() }
            }
    ) {
        Text(text = "Column height in pixel: $columnHeightPx")
        Text(text = "Column height in dp: $columnHeightDp")
    }
}
ctzwtxfj

ctzwtxfj2#

完整的解决方案如下:

@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")
            }
        }
    }
}
ddarikpa

ddarikpa3#

您也可以使用BoxWithConstraints,如下所示:

Button(onClick = {}){

   BoxWithConstraints{
     val height = maxHeight

   }
}

但我不确定它是否适合你的特定用途。

5gfr0r5j

5gfr0r5j4#

使用Modifier.onSizeChanged{}Modifier.globallyPositioned{}可能会导致无限的重组,如果你不小心,在OP的问题时,一个组合的大小影响另一个。
已更改大小(kotlin.Function1)
使用MutableState中的onSizeChanged大小值更新布局会导致读取新的大小值并在后续帧中重新组合布局,从而导致一帧滞后。
您可以使用onSizeChanged来影响绘制操作。使用Layout或SubcomposeLayout使一个组件的大小能够影响另一个组件的大小。
即使可以绘制,如果帧的变化是明显的用户,它不会看起来很好
比如说

Column {

    var sizeInDp by remember { mutableStateOf(DpSize.Zero) }
    val density = LocalDensity.current

    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绘图从一个维度更改为另一个维度的操作,则可能会看起来像闪光或故障。
获取元素高度而不进行重新组合的第一种方法是使用BoxWithConstraints
BoxWithConstraints 'BoxScope包含dp中的maxHeight和整数中的constraints.maxHeigh t。
但是,BoxWithConstraints在某些情况下返回大小不精确的约束,如使用Modifier. fillMaxHeight、没有任何大小修饰符或具有垂直滚动的父级返回不正确的值
您可以检出从BoxWithConstraints返回的尺寸的this answer,Constraints部分显示了使用BoxWithConstraints将获得的内容。
verticalScroll返回约束。无限高。
获取精确大小的可靠方法是使用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))
    }
)
vlurs2pr

vlurs2pr5#

我设法实现了一个变通方法,通过使从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 },
    )
}

相关问题