Stu*_*DTO 2 android kotlin android-jetpack android-jetpack-compose
我有一个Composable,LazyColumn但我正在尝试检查LazyListState我曾经使用过的可见性
fun LazyListState.visibleItems(itemVisiblePercentThreshold: Float) =
layoutInfo
.visibleItemsInfo
.filter {
visibilityPercent(it) >= itemVisiblePercentThreshold
}
private fun LazyListState.visibilityPercent(info: LazyListItemInfo): Float {
val cutTop = maxOf(0, layoutInfo.viewportStartOffset - info.offset)
val cutBottom = maxOf(0, info.offset + info.size - layoutInfo.viewportEndOffset)
return maxOf(0f, 100f - (cutTop + cutBottom) * 100f / info.size)
}
Run Code Online (Sandbox Code Playgroud)
但现在我想要它在里面,Composable因为我无权访问列表,其他功能使用我的Composables,它可能在 , 等里面LazyList,Surface所以我用它onGloballyPositioned来确定它是否可见,但我想了解是否至少有 30% 可见。任何想法?
我想为我的应用程序的其他功能提供一个@Composable,例如:
Feature1 有一个LazyColumn打印它的项目,但是他们想将 my 添加@Composable到列表的顶部,所以我想知道@Composable在这种情况下 my 何时可见(至少 30%),如果它通常位于顶部,则应该始终是加载列表时为 100%,但例如在功能 2 中,他们希望将我的添加@Composable到列表的中间,所以我应该知道它何时开始可见,这就是为什么我需要阈值来发送一些事件。
它不会总是 a,LazyColumn这就是为什么我不知道我是否想附加到 a ,但如果需要,我可以在我的 上LazyListState收到 a 。LazyListState@Composable
如果您想将其与任何 Compoasble 一起使用,您可以使用Modifier.onGloballyPositioned{}
fun Modifier.isVisible(
parentCoordinates: LayoutCoordinates?,
threshold: Int,
onVisibilityChange: (Boolean) -> Unit
) = composed {
val view = LocalView.current
Modifier.onGloballyPositioned { layoutCoordinates: LayoutCoordinates ->
if (parentCoordinates == null) return@onGloballyPositioned
val layoutHeight = layoutCoordinates.size.height
val thresholdHeight = layoutHeight * threshold / 100
val layoutTop = layoutCoordinates.positionInRoot().y
val parentTop = parentCoordinates.positionInParent().y
val parentHeight = parentCoordinates.size.height
val parentBottom = (parentTop + parentHeight).coerceAtMost(view.height.toFloat())
println(
"layoutTop: $layoutTop, " +
" parentTop: $parentTop, " +
" parentBottom: $parentBottom, " +
"parentHeight: $parentHeight, " +
"SECTION: ${parentBottom - layoutTop}"
)
if (
parentBottom - layoutTop > thresholdHeight &&
(layoutTop - parentTop > thresholdHeight - layoutHeight)
) {
onVisibilityChange(true)
} else {
onVisibilityChange(false)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我发布了一个有点复杂的示例来展示无论父级在其父级中的位置如何,您都可以获得位置。
您可以将其与 LazyColumn 或具有垂直滚动的 Column 一起使用。
@Preview
@Composable
private fun ScrollTest() {
var isVisible by remember {
mutableStateOf(false)
}
var coordinates by remember {
mutableStateOf<LayoutCoordinates?>(null)
}
val context = LocalContext.current
var visibleTime by remember {
mutableLongStateOf(0L)
}
LaunchedEffect(isVisible) {
if (isVisible) {
visibleTime = System.currentTimeMillis()
Toast.makeText(context, " Item 30% threshold is passed $isVisible", Toast.LENGTH_SHORT)
.show()
} else if (visibleTime != 0L) {
val currentTime = System.currentTimeMillis()
val totalTime = currentTime - visibleTime
Toast.makeText(context, " Item was visible for $totalTime ms", Toast.LENGTH_SHORT)
.show()
}
}
Column {
Box(modifier = Modifier.height(100.dp))
LazyColumn(
modifier = Modifier
.onPlaced { layoutCoordinates: LayoutCoordinates ->
coordinates = layoutCoordinates
}
.weight(1f)
.fillMaxSize()
.border(2.dp, Color.Black)
) {
items(60) { index: Int ->
if (index == 15) {
Column(
modifier = Modifier.fillMaxWidth().height(300.dp)
.border(6.dp, if (isVisible) Color.Green else Color.Red)
.isVisible(parentCoordinates = coordinates, threshold = 30) {
isVisible = it
}
) {
Box(modifier = Modifier.fillMaxWidth().weight(3f).background(Color.Yellow))
Box(modifier = Modifier.fillMaxWidth().weight(4f).background(Color.Cyan))
Box(modifier = Modifier.fillMaxWidth().weight(3f).background(Color.Magenta))
}
} else {
Text(
text = "Row $index",
fontSize = 24.sp,
modifier = Modifier.fillMaxWidth().padding(8.dp)
)
}
}
}
// Column(
// modifier = Modifier
// .onPlaced { layoutCoordinates: LayoutCoordinates ->
// coordinates = layoutCoordinates
// }
// .weight(1f)
// .fillMaxSize()
// .border(2.dp, Color.Black)
// .verticalScroll(rememberScrollState())
// ) {
// repeat(60) { index ->
// if (index == 15) {
// Column(
// modifier = Modifier.fillMaxWidth().height(300.dp)
// .border(6.dp, if (isVisible) Color.Green else Color.Red)
// .isVisible(parentCoordinates = coordinates, threshold = 30) {
// isVisible = it
// }
// ) {
// Box(modifier = Modifier.fillMaxWidth().weight(3f).background(Color.Yellow))
// Box(modifier = Modifier.fillMaxWidth().weight(4f).background(Color.Cyan))
// Box(modifier = Modifier.fillMaxWidth().weight(3f).background(Color.Magenta))
// }
// } else {
// Text(
// text = "Row $index",
// fontSize = 24.sp,
// modifier = Modifier.fillMaxWidth().padding(8.dp)
// )
// }
// }
// }
Box(modifier = Modifier.height(100.dp))
}
}
Run Code Online (Sandbox Code Playgroud)
fun Modifier.isVisible(
threshold: Int,
onVisibilityChange: (Boolean) -> Unit
) = composed {
Modifier.onGloballyPositioned { layoutCoordinates: LayoutCoordinates ->
val layoutHeight = layoutCoordinates.size.height
val thresholdHeight = layoutHeight * threshold / 100
val layoutTop = layoutCoordinates.positionInRoot().y
val layoutBottom = layoutTop + layoutHeight
// This should be parentLayoutCoordinates not parentCoordinates
val parent =
layoutCoordinates.parentLayoutCoordinates
parent?.boundsInRoot()?.let { rect: Rect ->
val parentTop = rect.top
val parentBottom = rect.bottom
if (
parentBottom - layoutTop > thresholdHeight &&
(parentTop < layoutBottom - thresholdHeight)
) {
onVisibilityChange(true)
} else {
onVisibilityChange(false)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您想将其用于多个可组合项,请创建一个自定义可组合项作为
@Composable
private fun MyCustomBox(
modifier: Modifier = Modifier,
threshold: Int = 30,
content: @Composable () -> Unit
) {
var isVisible by remember {
mutableStateOf(false)
}
val context = LocalContext.current
var visibleTime by remember {
mutableLongStateOf(0L)
}
LaunchedEffect(isVisible) {
if (isVisible) {
visibleTime = System.currentTimeMillis()
Toast.makeText(context, " Item 30% threshold is passed $isVisible", Toast.LENGTH_SHORT)
.show()
} else if (visibleTime != 0L) {
val currentTime = System.currentTimeMillis()
val totalTime = currentTime - visibleTime
Toast.makeText(context, " Item was visible for $totalTime ms", Toast.LENGTH_SHORT)
.show()
}
}
Box(
modifier = modifier
.border(6.dp, if (isVisible) Color.Green else Color.Red)
.isVisible(threshold = threshold) {
isVisible = it
}
) {
content()
}
}
Run Code Online (Sandbox Code Playgroud)
你可以用它作为
@Preview
@Composable
private fun ScrollTest2() {
Column {
TopAppBar {
Text("TopAppbar")
}
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.border(2.dp, Color.Black)
.verticalScroll(rememberScrollState())
) {
repeat(60) { index ->
if (index == 15 || index == 22 || index == 35) {
MyCustomBox(
modifier = Modifier.fillMaxWidth().height(300.dp)
) {
Column {
Box(
modifier = Modifier.fillMaxWidth().weight(3f)
.background(Color.Yellow)
)
Box(
modifier = Modifier.fillMaxWidth().weight(4f).background(Color.Cyan)
)
Box(
modifier = Modifier.fillMaxWidth().weight(3f)
.background(Color.Magenta)
)
}
}
} else {
Text(
text = "Row $index",
fontSize = 24.sp,
modifier = Modifier.fillMaxWidth().padding(8.dp)
)
}
}
}
Box(modifier = Modifier.height(100.dp))
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1004 次 |
| 最近记录: |