单击第一项时如何删除 DropdownMenu 的默认垂直填充?

A.G*_*A.G 1 android kotlin drop-down-menu android-jetpack-compose

我正在尝试实现一个简单的下拉菜单。一切正常,但当我单击第一个项目时,涟漪效应不会完全覆盖顶部的 DropDownMenu,最后一个项目也会发生同样的情况。

这是正在发生的事情的图像:

在此输入图像描述

这是我的代码:


  MaterialTheme(shapes=MaterialTheme.shapes.copy(medium = RoundedCornerShape(16.dp))) {

                DropdownMenu(
                    expanded = expanded,
                    onDismissRequest = { expanded = false },

                ) {
                    DropdownMenuItem(onClick = {
                        Toast.makeText(
                            context,
                            "Refresh Clicked",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                    ,


                    ) {
                        Text("Refresh")
                    }
                    DropdownMenuItem(onClick = {
                        Toast.makeText(
                            context,
                            "Setting Clicked",
                            Toast.LENGTH_SHORT
                        ).show()
                    }) {
                        Text("Settings")
                    }
                    Divider()
                    DropdownMenuItem(onClick = {
                        Toast.makeText(
                            context,
                            "Details Clicked",
                            Toast.LENGTH_SHORT
                        ).show()
                    }) {
                        Text("Details")
                    }


                }
Run Code Online (Sandbox Code Playgroud)

Mr.*_*mon 5

将文件复制到项目并调用 DropdownMenuNoPaddingVeitical

// Menu open/close animation.
const val InTransitionDuration = 120
const val OutTransitionDuration = 75
private val MenuElevation = 8.dp
private val MenuVerticalMargin = 48.dp

        @Composable
    fun DropdownMenuNoPaddingVeitical(
        expanded: Boolean,
        onDismissRequest: () -> Unit,
        modifier: Modifier = Modifier,
        offset: DpOffset = DpOffset(0.dp, 0.dp),
        properties: PopupProperties = PopupProperties(focusable = true),
        content: @Composable ColumnScope.() -> Unit
    ) {
        val expandedStates = remember { MutableTransitionState(false) }
        expandedStates.targetState = expanded
    
        if (expandedStates.currentState || expandedStates.targetState) {
            val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
            val density = LocalDensity.current
            val popupPositionProvider = DropdownMenuPositionProvider(
                offset,
                density
            ) { parentBounds, menuBounds ->
                transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds)
            }
    
            Popup(
                onDismissRequest = onDismissRequest,
                popupPositionProvider = popupPositionProvider,
                properties = properties
            ) {
                DropdownMenuContent(
                    expandedStates = expandedStates,
                    transformOriginState = transformOriginState,
                    modifier = modifier,
                    content = content
                )
            }
        }
    }
    
    @Composable
    fun DropdownMenuContent(
        expandedStates: MutableTransitionState<Boolean>,
        transformOriginState: MutableState<TransformOrigin>,
        modifier: Modifier = Modifier,
        content: @Composable ColumnScope.() -> Unit
    ) {
        // Menu open/close animation.
        val transition = updateTransition(expandedStates, "DropDownMenu")
    
        val scale by transition.animateFloat(
            transitionSpec = {
                if (false isTransitioningTo true) {
                    // Dismissed to expanded
                    tween(
                        durationMillis = InTransitionDuration,
                        easing = LinearOutSlowInEasing
                    )
                } else {
                    // Expanded to dismissed.
                    tween(
                        durationMillis = 1,
                        delayMillis = OutTransitionDuration - 1
                    )
                }
            }, label = ""
        ) {
            if (it) {
                // Menu is expanded.
                1f
            } else {
                // Menu is dismissed.
                0.8f
            }
        }
    
        val alpha by transition.animateFloat(
            transitionSpec = {
                if (false isTransitioningTo true) {
                    // Dismissed to expanded
                    tween(durationMillis = 30)
                } else {
                    // Expanded to dismissed.
                    tween(durationMillis = OutTransitionDuration)
                }
            }, label = ""
        ) {
            if (it) {
                // Menu is expanded.
                1f
            } else {
                // Menu is dismissed.
                0f
            }
        }
        Card(
            modifier = Modifier.graphicsLayer {
                scaleX = scale
                scaleY = scale
                this.alpha = alpha
                transformOrigin = transformOriginState.value
            },
            elevation = MenuElevation
        ) {
            Column(
                modifier = modifier
                    .width(IntrinsicSize.Max)
                    .verticalScroll(rememberScrollState()),
                content = content
            )
        }
    }
    
    
    private val MenuVerticalMargin = 48.dp
    
    data class DropdownMenuPositionProvider(
        val contentOffset: DpOffset,
        val density: Density,
        val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
    ) : PopupPositionProvider {
        override fun calculatePosition(
            anchorBounds: IntRect,
            windowSize: IntSize,
            layoutDirection: LayoutDirection,
            popupContentSize: IntSize
        ): IntOffset {
            // The min margin above and below the menu, relative to the screen.
            val verticalMargin = with(density) { MenuVerticalMargin.roundToPx() }
            // The content offset specified using the dropdown offset parameter.
            val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
            val contentOffsetY = with(density) { contentOffset.y.roundToPx() }
    
            // Compute horizontal position.
            val toRight = anchorBounds.left + contentOffsetX
            val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width
            val toDisplayRight = windowSize.width - popupContentSize.width
            val toDisplayLeft = 0
            val x = if (layoutDirection == LayoutDirection.Ltr) {
                sequenceOf(
                    toRight,
                    toLeft,
                    // If the anchor gets outside of the window on the left, we want to position
                    // toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight.
                    if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft
                )
            } else {
                sequenceOf(
                    toLeft,
                    toRight,
                    // If the anchor gets outside of the window on the right, we want to position
                    // toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft.
                    if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight
                )
            }.firstOrNull {
                it >= 0 && it + popupContentSize.width <= windowSize.width
            } ?: toLeft
    
            // Compute vertical position.
            val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
            val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
            val toCenter = anchorBounds.top - popupContentSize.height / 2
            val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
            val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
                it >= verticalMargin &&
                        it + popupContentSize.height <= windowSize.height - verticalMargin
            } ?: toTop
    
            onPositionCalculated(
                anchorBounds,
                IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
            )
            return IntOffset(x, y)
        }
    }
    
    fun calculateTransformOrigin(
        parentBounds: IntRect,
        menuBounds: IntRect
    ): TransformOrigin {
        val pivotX = when {
            menuBounds.left >= parentBounds.right -> 0f
            menuBounds.right <= parentBounds.left -> 1f
            menuBounds.width == 0 -> 0f
            else -> {
                val intersectionCenter =
                    (
                            kotlin.math.max(parentBounds.left, menuBounds.left) +
                                    kotlin.math.min(parentBounds.right, menuBounds.right)
                            ) / 2
                (intersectionCenter - menuBounds.left).toFloat() / menuBounds.width
            }
        }
        val pivotY = when {
            menuBounds.top >= parentBounds.bottom -> 0f
            menuBounds.bottom <= parentBounds.top -> 1f
            menuBounds.height == 0 -> 0f
            else -> {
                val intersectionCenter =
                    (
                            kotlin.math.max(parentBounds.top, menuBounds.top) +
                                    kotlin.math.min(parentBounds.bottom, menuBounds.bottom)
                            ) / 2
                (intersectionCenter - menuBounds.top).toFloat() / menuBounds.height
            }
        }
        return TransformOrigin(pivotX, pivotY)
    }
Run Code Online (Sandbox Code Playgroud)


All*_* Hu 5

这是我的方法。

Crop.kt

fun Modifier.crop(
    horizontal: Dp = 0.dp,
    vertical: Dp = 0.dp,
): Modifier = this.layout { measurable, constraints ->
    val placeable = measurable.measure(constraints)
    fun Dp.toPxInt(): Int = this.toPx().toInt()

    layout(
        placeable.width - (horizontal * 2).toPxInt(),
        placeable.height - (vertical * 2).toPxInt()
    ) {
        placeable.placeRelative(-horizontal.toPx().toInt(), -vertical.toPx().toInt())
    }
}
Run Code Online (Sandbox Code Playgroud)

我们在 上创建一个扩展函数Modifier。该函数实际上会垂直/水平地裁剪出任何可组合的内容。

我们可以轻松地裁剪掉 的顶部和底部部分DropdownMenu,然后添加回圆角,如下所示:

DropdownMenu(
    modifier = Modifier
        .crop(vertical = 8.dp)
        .clip(RoundedCornerShape(8.dp)),
    expanded = expanded,
    onDismissRequest = { expanded = false },
    content = content,
)
Run Code Online (Sandbox Code Playgroud)