有什么方法可以添加滚动条以添加LazyCol在列
(ScrollableCol在列
已弃用)。Javadoc没有提到任何关于Jetpack撰写中的滚动条的内容。
在Jetpack Compose中有可能做到吗?还是不支持滚动条?
我接受了@Dmitry的答案,并在此基础上构建:
固定的KnobRatio
参数的能力/**
* Renders a scrollbar.
*
* <ul> <li> A scrollbar is composed of two components: a track and a knob. The knob moves across
* the track <li> The scrollbar appears automatically when the user starts scrolling and disappears
* after the scrolling is finished </ul>
*
* @param state The [LazyListState] that has been passed into the lazy list or lazy row
* @param horizontal If `true`, this will be a horizontally-scrolling (left and right) scroll bar,
* if `false`, it will be vertically-scrolling (up and down)
* @param alignEnd If `true`, the scrollbar will appear at the "end" of the scrollable composable it
* is decorating (at the right-hand side in left-to-right locales or left-hand side in right-to-left
* locales, for the vertical scrollbars -or- the bottom for horizontal scrollbars). If `false`, the
* scrollbar will appear at the "start" of the scrollable composable it is decorating (at the
* left-hand side in left-to-right locales or right-hand side in right-to-left locales, for the
* vertical scrollbars -or- the top for horizontal scrollbars)
* @param thickness How thick/wide the track and knob should be
* @param fixedKnobRatio If not `null`, the knob will always have this size, proportional to the
* size of the track. You should consider doing this if the size of the items in the scrollable
* composable is not uniform, to avoid the knob from oscillating in size as you scroll through the
* list
* @param knobCornerRadius The corner radius for the knob
* @param trackCornerRadius The corner radius for the track
* @param knobColor The color of the knob
* @param trackColor The color of the track. Make it [Color.Transparent] to hide it
* @param padding Edge padding to "squeeze" the scrollbar start/end in so it's not flush with the
* contents of the scrollable composable it is decorating
* @param visibleAlpha The alpha when the scrollbar is fully faded-in
* @param hiddenAlpha The alpha when the scrollbar is fully faded-out. Use a non-`0` number to keep
* the scrollbar from ever fading out completely
* @param fadeInAnimationDurationMs The duration of the fade-in animation when the scrollbar appears
* once the user starts scrolling
* @param fadeOutAnimationDurationMs The duration of the fade-out animation when the scrollbar
* disappears after the user is finished scrolling
* @param fadeOutAnimationDelayMs Amount of time to wait after the user is finished scrolling before
* the scrollbar begins its fade-out animation
*/
@Composable
fun Modifier.scrollbar(
state: LazyListState,
horizontal: Boolean,
alignEnd: Boolean = true,
thickness: Dp = 4.dp,
fixedKnobRatio: Float? = null,
knobCornerRadius: Dp = 4.dp,
trackCornerRadius: Dp = 2.dp,
knobColor: Color = Color.Black,
trackColor: Color = Color.White,
padding: Dp = 0.dp,
visibleAlpha: Float = 1f,
hiddenAlpha: Float = 0f,
fadeInAnimationDurationMs: Int = 150,
fadeOutAnimationDurationMs: Int = 500,
fadeOutAnimationDelayMs: Int = 1000,
): Modifier {
check(thickness > 0.dp) { "Thickness must be a positive integer." }
check(fixedKnobRatio == null || fixedKnobRatio < 1f) {
"A fixed knob ratio must be smaller than 1."
}
check(knobCornerRadius >= 0.dp) { "Knob corner radius must be greater than or equal to 0." }
check(trackCornerRadius >= 0.dp) { "Track corner radius must be greater than or equal to 0." }
check(hiddenAlpha <= visibleAlpha) { "Hidden alpha cannot be greater than visible alpha." }
check(fadeInAnimationDurationMs >= 0) {
"Fade in animation duration must be greater than or equal to 0."
}
check(fadeOutAnimationDurationMs >= 0) {
"Fade out animation duration must be greater than or equal to 0."
}
check(fadeOutAnimationDelayMs >= 0) {
"Fade out animation delay must be greater than or equal to 0."
}
val targetAlpha =
if (state.isScrollInProgress) {
visibleAlpha
} else {
hiddenAlpha
}
val animationDurationMs =
if (state.isScrollInProgress) {
fadeInAnimationDurationMs
} else {
fadeOutAnimationDurationMs
}
val animationDelayMs =
if (state.isScrollInProgress) {
0
} else {
fadeOutAnimationDelayMs
}
val alpha by
animateFloatAsState(
targetValue = targetAlpha,
animationSpec =
tween(delayMillis = animationDelayMs, durationMillis = animationDurationMs))
return drawWithContent {
drawContent()
state.layoutInfo.visibleItemsInfo.firstOrNull()?.let { firstVisibleItem ->
if (state.isScrollInProgress || alpha > 0f) {
// Size of the viewport, the entire size of the scrollable composable we are decorating with
// this scrollbar.
val viewportSize =
if (horizontal) {
size.width
} else {
size.height
} - padding.toPx() * 2
// The size of the first visible item. We use this to estimate how many items can fit in the
// viewport. Of course, this works perfectly when all items have the same size. When they
// don't, the scrollbar knob size will grow and shrink as we scroll.
val firstItemSize = firstVisibleItem.size
// The *estimated* size of the entire scrollable composable, as if it's all on screen at
// once. It is estimated because it's possible that the size of the first visible item does
// not represent the size of other items. This will cause the scrollbar knob size to grow
// and shrink as we scroll, if the item sizes are not uniform.
val estimatedFullListSize = firstItemSize * state.layoutInfo.totalItemsCount
// The difference in position between the first pixels visible in our viewport as we scroll
// and the top of the fully-populated scrollable composable, if it were to show all the
// items at once. At first, the value is 0 since we start all the way to the top (or start
// edge). As we scroll down (or towards the end), this number will grow.
val viewportOffsetInFullListSpace =
state.firstVisibleItemIndex * firstItemSize + state.firstVisibleItemScrollOffset
// Where we should render the knob in our composable.
val knobPosition =
(viewportSize / estimatedFullListSize) * viewportOffsetInFullListSpace + padding.toPx()
// How large should the knob be.
val knobSize =
fixedKnobRatio?.let { it * viewportSize }
?: (viewportSize * viewportSize) / estimatedFullListSize
// Draw the track
drawRoundRect(
color = trackColor,
topLeft =
when {
// When the scrollbar is horizontal and aligned to the bottom:
horizontal && alignEnd -> Offset(padding.toPx(), size.height - thickness.toPx())
// When the scrollbar is horizontal and aligned to the top:
horizontal && !alignEnd -> Offset(padding.toPx(), 0f)
// When the scrollbar is vertical and aligned to the end:
alignEnd -> Offset(size.width - thickness.toPx(), padding.toPx())
// When the scrollbar is vertical and aligned to the start:
else -> Offset(0f, padding.toPx())
},
size =
if (horizontal) {
Size(size.width - padding.toPx() * 2, thickness.toPx())
} else {
Size(thickness.toPx(), size.height - padding.toPx() * 2)
},
alpha = alpha,
cornerRadius = CornerRadius(x = trackCornerRadius.toPx(), y = trackCornerRadius.toPx()),
)
// Draw the knob
drawRoundRect(
color = knobColor,
topLeft =
when {
// When the scrollbar is horizontal and aligned to the bottom:
horizontal && alignEnd -> Offset(knobPosition, size.height - thickness.toPx())
// When the scrollbar is horizontal and aligned to the top:
horizontal && !alignEnd -> Offset(knobPosition, 0f)
// When the scrollbar is vertical and aligned to the end:
alignEnd -> Offset(size.width - thickness.toPx(), knobPosition)
// When the scrollbar is vertical and aligned to the start:
else -> Offset(0f, knobPosition)
},
size =
if (horizontal) {
Size(knobSize, thickness.toPx())
} else {
Size(thickness.toPx(), knobSize)
},
alpha = alpha,
cornerRadius = CornerRadius(x = knobCornerRadius.toPx(), y = knobCornerRadius.toPx()),
)
}
}
}
}
这在懒散列
/懒人行
中还不可能实现。
它计划在某个时候被添加,但是还没有一个具体的发布计划。有可能的时候我会更新这个回答。
现在实际上是可能的(他们已经在LazyListState中添加了更多内容)并且非常容易做到。这是一个非常原始的滚动条(始终可见/无法拖动/等),它使用项目索引来计算拇指位置,因此在只有少数项目的列表中滚动时可能看起来不太好:
@Composable
fun Modifier.simpleVerticalScrollbar(
state: LazyListState,
width: Dp = 8.dp
): Modifier {
val targetAlpha = if (state.isScrollInProgress) 1f else 0f
val duration = if (state.isScrollInProgress) 150 else 500
val alpha by animateFloatAsState(
targetValue = targetAlpha,
animationSpec = tween(durationMillis = duration)
)
return drawWithContent {
drawContent()
val firstVisibleElementIndex = state.layoutInfo.visibleItemsInfo.firstOrNull()?.index
val needDrawScrollbar = state.isScrollInProgress || alpha > 0.0f
// Draw scrollbar if scrolling or if the animation is still running and lazy column has content
if (needDrawScrollbar && firstVisibleElementIndex != null) {
val elementHeight = this.size.height / state.layoutInfo.totalItemsCount
val scrollbarOffsetY = firstVisibleElementIndex * elementHeight
val scrollbarHeight = state.layoutInfo.visibleItemsInfo.size * elementHeight
drawRect(
color = Color.Red,
topLeft = Offset(this.size.width - width.toPx(), scrollbarOffsetY),
size = Size(width.toPx(), scrollbarHeight),
alpha = alpha
)
}
}
}
UPD:我已经更新了代码。我已经弄清楚了如何在LazyColumn滚动或未添加淡入/淡出动画时显示/隐藏滚动条。我还将drawBehind()更改为drawWithContent(),因为前者在内容后面绘制,因此在某些情况下,它可能会在滚动条的顶部绘制,这很可能是不希望的。
我使用的是底部导航栏,每个菜单将用户导航到特定的可组合屏幕。我使用了导航库来管理它们之间的导航。 我正在为所有可组合使用通用的ViewModel。我正在其中一个可组合中使用懒惰列,当我在底部导航栏中单击菜单项在菜单项之间导航时,它会按预期工作,在懒惰列的滚动位置保存的位置。 当我在具有一个lazyColumn的可组合屏幕中单击按钮(我已经将它编程为导航到导航图中的起始目的地)并导航回该按钮时,问题
在我的主页上,我有一个懒散的专栏,其中一个项目是水平寻呼机。在每个水平寻呼机中都有一些页面,我也需要在其中设置懒散栏。错误是不允许在同一方向上使用嵌套滚动。我应该如何实现这个ui?
我试图确保有一个视觉指示,表明用户正在尝试滚动到底部,即使他已经到达了LazyColumn中列表的末尾。 默认情况下,此功能在XML布局中可用。 我们如何在Jetpack compose中实现这一点?对于顶部的过度滚动,我看到有一个“滑动刷新”等效项。目前有解决方案吗?
上周我更新了Kotlin 1.5,昨天看到谷歌打算让Jetpack成为设计UI的首选选项后,我想做一些测试。 问题是将我的项目更新为静态编程语言1.5,当尝试构建项目时,我得到以下错误: 静态编程语言1.5与Jetpack Compose不兼容吗?在谷歌搜索问题后,我找到了版本,其中提到了Jetpack Compose,但不是以“不兼容”的方式。 你对此有任何答案吗?我应该使用吗?在这种情况下,我
我正在开发一款小型jetpack compose演示聊天应用程序。所以我需要在底部有一个带文本字段和发送按钮的栏,就像WhatsApp一样……我想最好使用带底部栏的脚手架。 现在的问题是,当键盘打开时,底部栏隐藏在键盘后面。有办法吗?
我想以编程方式更改当用户滚动下面列表中的每个“查看更多”项目时选择的选项卡。我如何才能最好地完成这项工作?