我从Kotlin Compose for Desktop中 Select 了一个Dropdown Menu,我想包括一个垂直滚动条.下拉菜单的源代码是here.他们有一个sample,可以很好地工作,但我无法让它显示垂直滚动条.默认情况下不会显示.

还有一个VerticalScrollbar的例子,我也试过了,但我还没有让它与DropdownMenu一起工作.

verticalScroll()放在DropdownMenu(Modifier)中会导致错误Vertically scrolled component was measured with an infinity maximum height constraints, which is disallowed....

DropdownMenu下面加上VerticalScrollbar()会导致错误Can't represent a size of 1073741824 in Constraints.

因此,据我所知,在DropdownMenu米S Popup米中,有一些东西让我很难做到这一点.

有没有办法实现可见的滚动条?到目前为止,我的布局是这样的.(您可以在下面的下拉菜单中 Select scroll)

data class Lookup(val id: String, val name: String)

fun main() = application {
   Window(
      onCloseRequest = ::exitApplication,
      state = rememberWindowState(width = 1280.dp, height = 800.dp)
   ) {
      MaterialTheme {
         Scaffold {
            Column(
               modifier = Modifier
                  .fillMaxSize()
                  .verticalScroll(rememberScrollState()),
               horizontalAlignment = Alignment.CenterHorizontally
            ) {
               val lookups = LookupService.getLookups() // about 75 items
               val (expanded, setExpanded) = remember { mutableStateOf(false) }
               val (selected, setSelected) = remember { mutableStateOf<Lookup?>(null) }

               Spacer(Modifier.height(20.dp))
               Box(Modifier.wrapContentSize(Alignment.TopStart)) {
                  val icon = if (expanded) {
                     Icons.Filled.KeyboardArrowUp
                  } else {
                     Icons.Filled.KeyboardArrowDown
                  }
                  OutlinedTextField(
                     value = selected?.name ?: "",
                     onValueChange = { },
                     modifier = Modifier
                        .width(360.dp)
                        .onKeyEvent {
                           if (it.key == Key.DirectionDown && !expanded) {
                              setExpanded(true)
                              return@onKeyEvent true
                           }
                           return false
                        }
                        .clickable { setExpanded(true) },
                     singleLine = true,
                     label = { Text("Select an item") },
                     trailingIcon = {
                        Icon(icon, "Select an item", Modifier.clickable {
                           setExpanded(!expanded)
                        })
                     },
                     enabled = expanded,
                     colors = TextFieldDefaults.textFieldColors(
                        disabledTextColor = LocalContentColor.current.copy(LocalContentAlpha.current),
                        backgroundColor = Color.Transparent
                     )
                  )

                  DropdownMenu( // Desktop version, so it creates a "Popup" per the source code
                     expanded = expanded,
                     onDismissRequest = {
                        runBlocking { // to handle a glitch where the dropdown may "unexpand & expand" again on clicking
                           delay(200)
                           setExpanded(false)
                        }
                     },
                     modifier = Modifier
                        .width(360.dp)
                        .background(Color.White)
                        .clip(RoundedCornerShape(5.dp))
                     // SHOULD HAVE VERTICAL SCROLLBAR SHOW UP AS PART OF THIS DROPDOWNMENU COLUMN
                  ) {
                     lookups.forEach { lookup -> 
                        DropdownMenuItem(
                           onClick = {
                              setExpanded(false)
                              setSelected(lookup)
                           }
                        ) {
                           Text(lookup.name)
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

推荐答案

我在目前公开的第Scrollbar doesn't work for DropdownMenu #587期中找到了答案.

这就是我如何让我的滚动条在桌面版本的下拉菜单中工作.

  1. 将此版本的DesktopMenu.desktop.kt复制到您的项目中.
  2. 将此版本的Menu.kt复制到您的项目中.
  3. 将这Card添加到您的MenU.S.kt中,围绕显示内容的Column.
  4. 按照问题中的this comment,从第3点开始将这一点添加到Card中.
Box(
    modifier = modifier
        .width(IntrinsicSize.Max)
) {
    val scrollState = rememberScrollState()
    var columnSize by remember { mutableStateOf<IntSize?>(null) }
    Column(
        modifier = Modifier
            .padding(vertical = DropdownMenuVerticalPadding)
            .verticalScroll(scrollState)
            .onSizeChanged { size ->
                columnSize = size
            },
        content = content
    )
    columnSize?.let { size ->
        VerticalScrollbar(
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .height(with(LocalDensity.current) { size.height.toDp() }),
            scrollState = scrollState
        )
    }
}
  1. 最后一点是,这将溢出我的屏幕,所以我返回到当前版本的DesktopMenu.desktop.kt,将这个版本的DesktopDropdownMenuPositionProvider放回到我的DesktopMenu.desktop.kt中.
/**
 * Positions a dropdown relative to another widget (its anchor).
 */
@Immutable
internal data class DesktopDropdownMenuPositionProvider(
    val contentOffset: DpOffset,
    val density: Density,
    val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
) : PopupPositionProvider {
    override fun calculatePosition(
        anchorBounds: IntRect,
        windowSize: IntSize,
        layoutDirection: LayoutDirection,
        popupContentSize: IntSize
    ): IntOffset {

        val isLtr = layoutDirection == LayoutDirection.Ltr

        // Coerce such that this..this+size fits into min..max; if impossible, align with min
        fun Int.coerceWithSizeIntoRangePreferMin(size: Int, min: Int, max: Int) = when {
            this < min -> min
            this + size > max -> max - size
            else -> this
        }

        // Coerce such that this..this+size fits into min..max; if impossible, align with max
        fun Int.coerceWithSizeIntoRangePreferMax(size: Int, min: Int, max: Int) = when {
            this + size > max -> max - size
            this < min -> min
            else -> this
        }

        fun Int.coerceWithSizeIntoRange(size: Int, min: Int, max: Int) = when {
            isLtr -> coerceWithSizeIntoRangePreferMin(size, min, max)
            else -> coerceWithSizeIntoRangePreferMax(size, min, max)
        }

        // 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 preferredX = if (isLtr) {
            anchorBounds.left + contentOffsetX
        }
        else {
            anchorBounds.right - contentOffsetX - popupContentSize.width
        }
        val x = preferredX.coerceWithSizeIntoRange(
            size = popupContentSize.width,
            min = 0,
            max = windowSize.width
        )

        // 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 toWindowBottom = windowSize.height - popupContentSize.height - verticalMargin
        var y = sequenceOf(toBottom, toTop, toCenter, toWindowBottom).firstOrNull {
            it >= verticalMargin &&
                it + popupContentSize.height <= windowSize.height - verticalMargin
        } ?: toTop

        // Desktop specific vertical position checking
        val aboveAnchor = anchorBounds.top + contentOffsetY
        val belowAnchor = windowSize.height - anchorBounds.bottom - contentOffsetY

        if (belowAnchor >= aboveAnchor) {
            y = anchorBounds.bottom + contentOffsetY
        }

        if (y + popupContentSize.height > windowSize.height) {
            y = windowSize.height - popupContentSize.height
        }

        y = y.coerceAtLeast(0)

        onPositionCalculated(
            anchorBounds,
            IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
        )
        return IntOffset(x, y)
    }
}

Kotlin相关问答推荐

为什么Kotlin函数参数名会 destruct 方法调用?

在Kotlin中将String转换为T

新的jOOQ Gradle插件无法正确处理自引用关系

Kotlin中是否可以混合使用推断和显式的通用类型参数?

Flow.state In() 未从其来源接收新值

Kotlin 启动与启动(Dispatchers.Default)

使用启动或使用 coroutineScope 启动协程之间的区别

在 Kotlin 协程中切换 IO 和 UI 的正确方法是什么?

如何使用 Kotlin KClass 属性 simpleName 生成空值

在 kotlin 中模拟伴随对象函数

Kotlin 代码是如何编译成原生代码的?

是否可以在 kotlin 中嵌套数据类?

Android Room - error: Cannot figure out how to save this field into database

作为 Kotlin 中的函数的结果,如何从 Firestore 数据库返回列表?

Kotlin协程处理错误和实现

将ExpectedException与Kotlin一起使用

如何在kotlin语言中将字符转换为ascii值

Kotlin lambda 语法混淆

无法解决:androidx.lifecycle:lifecycle-extensions-ktx:2.0.0-alpha1

为什么在 Kotlin 中return可以返回一个return?