创意背景
在安卓 View 传统命令式开发里面提供了非常多稳定美观体验好的组件,但是目前Compose还未有可用的组件,比如View中可以使用 coordinatorlayout 的滚动效果可以实现局部(即使内容不满一屏也可以触发滚动边界阻尼效果),为了最小成本实现相同的效果,我们可以利用Column的滚动边界为基础间接实现类似效果。
代码和过程:
package lcppx.
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
// 默认垂直滚动的竖直布局,在元素不满屏幕的时候,也可以滚动边界动画
// 原理就是不满屏幕的时候,多加0.5px,就可以触发滚动边界动画
@Composable
fun ColumnScrollV(
modifier: Modifier = Modifier,
// 垂直滚动参数配置
state: ScrollState = rememberScrollState(),
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false,
// 内容
content: @Composable() (ColumnScope.() -> Unit),
) {
state.maxValue
val isNotFull by remember { derivedStateOf { !state.canScrollForward && !state.canScrollBackward } }
//println("ColumnScrollV滚动是否满屏测试:$isNotFull")
BoxWithConstraints(modifier = modifier) {
val maxH by remember { derivedStateOf { maxHeight } }
// 如果不是满屏,就多加一个0.5像素,触发边界动画
Column(
modifier = Modifier.fillMaxWidth().height(maxHeight)
// 设置垂直滚动,如果不满屏幕,内外两个列表都使用同一个滚动状态,确保一起滚动
// 如果满屏,就使用两个不同的滚动状态,确保两个列表可以分开滚动(其实如果内部满屏可以滚动,就会屏蔽外部的滚动,所以此处只有设置不一样的状态即可)
.verticalScroll(if (isNotFull)state else rememberScrollState()),
) {
//val etr = 0.142.dp
val etrPx = 0.51f// 触发边缘滚动的阈值是0.5像素,稍微大一些
val etr = with(LocalDensity.current) { etrPx.toDp() }
//println("当前px值测试:$etrPx")
Column(
modifier = Modifier.fillMaxWidth().height(maxH).verticalScroll(state)// 设置垂直滚动
//.background(Color.Magenta),
) {
content()
}
//content()
Box(modifier = Modifier.fillMaxWidth().height(etr)
//.background(Color.Blue)
)
}
}
}
/
// 预览测试
@Preview
@Composable
private fun ColumnScrollVPreview() {
ColumnScrollV(
content = {
Column(modifier = Modifier.fillMaxWidth().height(50.dp).background(Color.Red)) {
Text(text = "1")
Text(text = "2")
Text(text = "3")
}
Column(modifier = Modifier.fillMaxWidth().height(50.dp).background(Color.Yellow)) {
Text(text = "1")
Text(text = "2")
}
},
)
}