我做了可滚动的内容,但是如何添加
scrollbarThumbVertical
?
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())//how to add here scrollbar?
.weight(1f, fill = false)
) {
//content
}
//another not scrollable content
}
添加滚动条以及verticalScroll或horizontalScroll修饰符(支持LTR和RTL布局方向)。您可以使用scrollbarConfig参数配置滚动条。
fun Modifier.scrollbar(
state: ScrollState,
direction: Orientation,
indicatorThickness: Dp = 8.dp,
indicatorColor: Color = Color.LightGray,
alpha: Float = if (state.isScrollInProgress) 0.8f else 0f,
alphaAnimationSpec: AnimationSpec<Float> = tween(
delayMillis = if (state.isScrollInProgress) 0 else 1500,
durationMillis = if (state.isScrollInProgress) 150 else 500
),
padding: PaddingValues = PaddingValues(all = 0.dp)
): Modifier = composed {
val scrollbarAlpha by animateFloatAsState(
targetValue = alpha,
animationSpec = alphaAnimationSpec
)
drawWithContent {
drawContent()
val showScrollBar = state.isScrollInProgress || scrollbarAlpha > 0.0f
// Draw scrollbar only if currently scrolling or if scroll animation is ongoing.
if (showScrollBar) {
val (topPadding, bottomPadding, startPadding, endPadding) = listOf(
padding.calculateTopPadding().toPx(), padding.calculateBottomPadding().toPx(),
padding.calculateStartPadding(layoutDirection).toPx(),
padding.calculateEndPadding(layoutDirection).toPx()
)
val contentOffset = state.value
val viewPortLength = if (direction == Orientation.Vertical)
size.height else size.width
val viewPortCrossAxisLength = if (direction == Orientation.Vertical)
size.width else size.height
val contentLength = max(viewPortLength + state.maxValue, 0.001f) // To prevent divide by zero error
val indicatorLength = ((viewPortLength / contentLength) * viewPortLength) - (
if (direction == Orientation.Vertical) topPadding + bottomPadding
else startPadding + endPadding
)
val indicatorThicknessPx = indicatorThickness.toPx()
val scrollOffsetViewPort = viewPortLength * contentOffset / contentLength
val scrollbarSizeWithoutInsets = if (direction == Orientation.Vertical)
Size(indicatorThicknessPx, indicatorLength)
else Size(indicatorLength, indicatorThicknessPx)
val scrollbarPositionWithoutInsets = if (direction == Orientation.Vertical)
Offset(
x = if (layoutDirection == LayoutDirection.Ltr)
viewPortCrossAxisLength - indicatorThicknessPx - endPadding
else startPadding,
y = scrollOffsetViewPort + topPadding
)
else
Offset(
x = if (layoutDirection == LayoutDirection.Ltr)
scrollOffsetViewPort + startPadding
else viewPortLength - scrollOffsetViewPort - indicatorLength - endPadding,
y = viewPortCrossAxisLength - indicatorThicknessPx - bottomPadding
)
drawRoundRect(
color = indicatorColor,
cornerRadius = CornerRadius(
x = indicatorThicknessPx / 2, y = indicatorThicknessPx / 2
),
topLeft = scrollbarPositionWithoutInsets,
size = scrollbarSizeWithoutInsets,
alpha = scrollbarAlpha
)
}
}
}
data class ScrollBarConfig(
val indicatorThickness: Dp = 8.dp,
val indicatorColor: Color = Color.LightGray,
val alpha: Float? = null,
val alphaAnimationSpec: AnimationSpec<Float>? = null,
val padding: PaddingValues = PaddingValues(all = 0.dp)
)
fun Modifier.verticalScrollWithScrollbar(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false,
scrollbarConfig: ScrollBarConfig = ScrollBarConfig()
) = this
.scrollbar(
state, Orientation.Vertical,
indicatorThickness = scrollbarConfig.indicatorThickness,
indicatorColor = scrollbarConfig.indicatorColor,
alpha = scrollbarConfig.alpha ?: if (state.isScrollInProgress) 0.8f else 0f,
alphaAnimationSpec = scrollbarConfig.alphaAnimationSpec ?: tween(
delayMillis = if (state.isScrollInProgress) 0 else 1500,
durationMillis = if (state.isScrollInProgress) 150 else 500
),
padding = scrollbarConfig.padding
)
.verticalScroll(state, enabled, flingBehavior, reverseScrolling)
fun Modifier.horizontalScrollWithScrollbar(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false,
scrollbarConfig: ScrollBarConfig = ScrollBarConfig()
) = this
.scrollbar(
state, Orientation.Horizontal,
indicatorThickness = scrollbarConfig.indicatorThickness,
indicatorColor = scrollbarConfig.indicatorColor,
alpha = scrollbarConfig.alpha ?: if (state.isScrollInProgress) 0.8f else 0f,
alphaAnimationSpec = scrollbarConfig.alphaAnimationSpec ?: tween(
delayMillis = if (state.isScrollInProgress) 0 else 1500,
durationMillis = if (state.isScrollInProgress) 150 else 500
),
padding = scrollbarConfig.padding
)
.horizontalScroll(state, enabled, flingBehavior, reverseScrolling)
使用示例:
Column(
Modifier
.fillMaxWidth()
.heightIn(max = 300.dp)
.padding(4.dp)
.border(1.dp, Color.Gray, RoundedCornerShape(8.dp))
.verticalScrollWithScrollbar(
scrollState,
scrollbarConfig = ScrollBarConfig(
padding = PaddingValues(4.dp, 4.dp, 4.dp, 4.dp)
)
)
.padding(2.dp)
) {
Text(
text = "Some very long scrollable text",
color = Color.Gray,
modifier = Modifier.padding(vertical = 4.dp)
)
}
补充 Abhijith Shambu 的答案,我想补充一点,为了使指示栏更大,您应该以这种方式将所需的大小添加到 viewPortLength 和scrollBarSizeWithoutInsets 变量中:
val scrollbarSizeWithoutInsets = if (direction == Orientation.Vertical)
Size(indicatorThicknessPx, (indicatorLength + indicatorSize.toPx()))
else Size(indicatorLength, indicatorThicknessPx)
val viewPortLength = if (direction == Orientation.Vertical)
size.height - indicatorSize.toPx() else size.width
这是我受此answer启发而实现的最小设计的垂直滚动条:
@Composable
fun Modifier.verticalScrollBar(
state: ScrollState,
color: Color = Color.Gray,
ratio: Float = 3f,
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 needDrawScrollbar = state.isScrollInProgress || alpha > 0.0f
val barHeight = (this.size.height / ratio)
val barRange = (this.size.height - barHeight) / state.maxValue
if (needDrawScrollbar) {
val position = state.value * barRange
drawRect(
color = color.copy(alpha = alpha),
topLeft = Offset(this.size.width - width.toPx(), position),
size = Size(width.toPx(), barHeight)
)
}
}
}
ratio
参数可用于更改条形高度,我根据列内元素的数量使用它。要使用它,只需将此修饰符添加到具有 verticalScroll()
修饰符的列,并将相同的 ScrollState 传递给它们。