如何在Android Jetpack Compose的Column或Row中添加渐变边缘效果?

19

我需要使用带有顶部渐变效果的LazyColumn。在Android上,我使用ListView或RecyclerView的淡入渐出渐变,但是在Jetpack Compose中找不到任何解决方案!

enter image description here

我尝试修改画布:

@Composable
fun Screen() {
    Box(
        Modifier
            .fillMaxWidth()
            .background(color = Color.Yellow)
    ) {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .drawWithContent {
                    val colors = listOf(Color.Transparent, Color.Black)
                    drawContent()
                    drawRect(
                        brush = Brush.verticalGradient(colors),
                        blendMode = BlendMode.DstIn
                    )
                }
        ) {
            itemsIndexed((1..1000).toList()) { item, index ->
                Text(
                    text = "Item $item: $index value",
                    modifier = Modifier.padding(12.dp),
                    color = Color.Red,
                    fontSize = 24.sp
                )
            }
        }
    }
}

但是得到了错误的结果:

在此输入图片描述


brush = Brush.verticalGradient(colors, endY = 500f) 笔刷 = 笔刷.垂直渐变(颜色, endY=500f) - user3872620
请将以下有关编程的内容从英语翻译成中文。仅返回翻译后的文本:相关:https://dev59.com/5sHqa4cB1Zd3GeqP4670 -edge-行为-in-jetpack-compose - Ji Sungbin
6个回答

17

在Compose 1.4.0-alpha02中引入了CompositingStrategy。这意味着不再需要使用.graphicsLayer { alpha = 0.99f }的hack了。相反,您可以使用.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)

这是一个可定制的淡出边缘修饰符:

fun Modifier.fadingEdge(brush: Brush) = this
    .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
    .drawWithContent {
        drawContent()
        drawRect(brush = brush, blendMode = BlendMode.DstIn)
    }

一些例子:

@Composable
fun FadingEdgeExamples() {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.SpaceEvenly,
        modifier = Modifier.fillMaxSize().background(Color.White)
    ) {

        val topFade = Brush.verticalGradient(0f to Color.Transparent, 0.3f to Color.Red)
        Box(modifier = Modifier.fadingEdge(topFade).background(Color.Blue).size(100.dp))

        val topBottomFade = Brush.verticalGradient(0f to Color.Transparent, 0.3f to Color.Red, 0.7f to Color.Red, 1f to Color.Transparent)
        Box(modifier = Modifier.fadingEdge(topBottomFade).background(Color.Blue).size(100.dp))

        val leftRightFade = Brush.horizontalGradient(0f to Color.Transparent, 0.1f to Color.Red, 0.9f to Color.Red, 1f to Color.Transparent)
        Box(modifier = Modifier.fadingEdge(leftRightFade).background(Color.Blue).size(100.dp))

        val radialFade = Brush.radialGradient(0f to Color.Red, 0.5f to Color.Transparent)
        Box(modifier = Modifier.fadingEdge(radialFade).background(Color.Blue).size(100.dp))

    }
}

注意:在指定渐变颜色时,Color.Transparent 定义了淡化发生的位置,而任何其他颜色(在这种情况下是 Color.Red,无论您选择哪个)都定义了内容。
输出: {{link1:examples}}

在我的情况下,LazyRow 中有两个元素,由于屏幕的大小,第二个元素默认情况下会被裁剪掉,没有任何淡入淡出的效果。而我想要实现的是,当有足够的空间来显示它们时,不提供淡入淡出效果(仅在视觉上,而不是在代码上);当空间不足时,提供淡入淡出效果。所以我只需将 LazyRow 包装在一个带有你上面提到的 fadingEdge 修饰符的 Box 中,并在 LazyRow 的头部和尾部添加两个 Spacer。这样就实现了我的目标。 - undefined

8

快速修复问题的方法:在您的修饰符中添加.graphicsLayer { alpha = 0.99f }

由于性能原因(如此处所述,参见“自定义修饰符”部分),Jetpack Compose默认禁用alpha合成。没有alpha合成,影响透明度的混合模式(例如DstIn)将无法产生预期的效果。目前最好的解决方法是在LazyColumn的修饰符中添加.graphicsLayer { alpha = 0.99F };这会通过使LazyColumn变得几乎不可感知地透明,迫使Jetpack Compose启用alpha合成。

使用此更改后,您的代码如下所示:

@Composable
fun Screen() {
    Box(
        Modifier
            .fillMaxWidth()
            .background(color = Color.Yellow)
    ) {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                // Workaround to enable alpha compositing
                .graphicsLayer { alpha = 0.99F }
                .drawWithContent {
                    val colors = listOf(Color.Transparent, Color.Black)
                    drawContent()
                    drawRect(
                        brush = Brush.verticalGradient(colors),
                        blendMode = BlendMode.DstIn
                    )
                }
        ) {
            itemsIndexed((1..1000).toList()) { item, index ->
                Text(
                    text = "Item $item: $index value",
                    modifier = Modifier.padding(12.dp),
                    color = Color.Red,
                    fontSize = 24.sp
                )
            }
        }
    }
}

生成正确的结果


在Compose 1.4.0-alpha02中引入了CompositingStrategy。这意味着不再需要使用.graphicsLayer { alpha = 0.99f } hack。相反,您可以使用.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)。有关更多信息,请参见我的答案 - nhcodes

7
你可以在列表顶部放置一个Spacer,并在该框上绘制渐变。将框的大小调小,以便仅有列表的一小部分有覆盖效果。将颜色设置为屏幕背景的颜色,这样看起来就像内容正在淡出。
val screenBackgroundColor = MaterialTheme.colors.background

    Box(Modifier.fillMaxSize()) {

        LazyColumn(Modifier.fillMaxSize()) {
            //your items
        }

        //Gradient overlay
        Spacer(
            Modifier
                .fillMaxWidth()
                .height(32.dp)
                .background(
                    brush = Brush.verticalGradient(
                        colors = listOf(
                            Color.Transparent,
                            screenBackgroundColor
                        )
                    )
                )
                //.align(Alignment) to control the position of the overlay
        )
    }

这是它的外观:

screenshot

然而,这似乎不完全符合您的要求,因为您似乎希望实际列表内容淡出。我不知道如何仅将 alpha 应用于视图的一部分。也许尝试挖掘 .alpha 源代码以找出答案。

3

在正确的方向上进行一点微调。这段代码的作用是在您的LazyColumn顶部放置一个带有淡入淡出修饰符的Box组合,可以在Column中再创建多个此类Box组合,以创建更平滑的效果。

@Composable
fun FadingExample() {
    Box(
        Modifier
            .fillMaxWidth()
            .requiredHeight(500.dp)) {

        LazyColumn(Modifier.fillMaxSize()) {

        }

        Box(
            Modifier
                .fillMaxWidth()
                .height(10.dp)
                .alpha(0.5f)
                .background(Color.Transparent)
                .align(Alignment.TopCenter)
        ) {

        }
    }
}

3
我优化了@user3872620的解决方案。你只需要将这些代码放在你的LazyColumn、VerticalPager下面,然后适当调整偏移量和高度,通常偏移量等于高度。
Box(
    Modifier
        .fillMaxWidth()
        .offset(y= (-10).dp)
        .height(10.dp)
        .background(brush = Brush.verticalGradient(
            colors = listOf(
                Color.Transparent,
                MaterialTheme.colors.background
            )
        ))
)

您将获得此渲染效果: 这是渲染效果

-1
这是一个非常简单的FadingEdgeLazyColumn实现,使用AndroidView。在LazyColumn的顶部和底部放置应用了渐变背景的AndroidView
@Stable
object GradientDefaults {
    @Stable
    val Color = androidx.compose.ui.graphics.Color.Black

    @Stable
    val Height = 30.dp
}

@Stable
sealed class Gradient {
    @Immutable
    data class Top(
        val color: Color = GradientDefaults.Color,
        val height: Dp = GradientDefaults.Height,
    ) : Gradient()

    @Immutable
    data class Bottom(
        val color: Color = GradientDefaults.Color,
        val height: Dp = GradientDefaults.Height,
    ) : Gradient()
}

@Composable
fun FadingEdgeLazyColumn(
    modifier: Modifier = Modifier,
    gradients: Set<Gradient> = setOf(Gradient.Top(), Gradient.Bottom()),
    contentGap: Dp = 0.dp,
    state: LazyListState = rememberLazyListState(),
    contentPadding: PaddingValues = PaddingValues(0.dp),
    reverseLayout: Boolean = false,
    verticalArrangement: Arrangement.Vertical =
        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
    userScrollEnabled: Boolean = true,
    content: LazyListScope.() -> Unit,
) {
    val topGradient =
        remember(gradients) { gradients.find { it is Gradient.Top } as? Gradient.Top }
    val bottomGradient =
        remember(gradients) { gradients.find { it is Gradient.Bottom } as? Gradient.Bottom }

    ConstraintLayout(modifier = modifier) {
        val (topGradientRef, lazyColumnRef, bottomGradientRef) = createRefs()

        GradientView(
            modifier = Modifier
                .constrainAs(topGradientRef) {
                    top.linkTo(parent.top)
                    width = Dimension.matchParent
                    height = Dimension.value(topGradient?.height ?: GradientDefaults.Height)
                }
                .zIndex(2f),
            colors = intArrayOf(
                (topGradient?.color ?: GradientDefaults.Color).toArgb(),
                Color.Transparent.toArgb()
            ),
            visible = topGradient != null
        )
        LazyColumn(
            modifier = Modifier
                .constrainAs(lazyColumnRef) {
                    top.linkTo(
                        anchor = topGradientRef.top,
                        margin = when (topGradient != null) {
                            true -> contentGap
                            else -> 0.dp
                        }
                    )
                    bottom.linkTo(
                        anchor = bottomGradientRef.bottom,
                        margin = when (bottomGradient != null) {
                            true -> contentGap
                            else -> 0.dp
                        }
                    )
                    width = Dimension.matchParent
                    height = Dimension.fillToConstraints
                }
                .zIndex(1f),
            state = state,
            contentPadding = contentPadding,
            reverseLayout = reverseLayout,
            verticalArrangement = verticalArrangement,
            horizontalAlignment = horizontalAlignment,
            flingBehavior = flingBehavior,
            userScrollEnabled = userScrollEnabled,
            content = content
        )
        GradientView(
            modifier = Modifier
                .constrainAs(bottomGradientRef) {
                    bottom.linkTo(parent.bottom)
                    width = Dimension.matchParent
                    height = Dimension.value(bottomGradient?.height ?: GradientDefaults.Height)
                }
                .zIndex(2f),
            colors = intArrayOf(
                Color.Transparent.toArgb(),
                (bottomGradient?.color ?: GradientDefaults.Color).toArgb(),
            ),
            visible = bottomGradient != null
        )
    }
}

@Composable
private fun GradientView(
    modifier: Modifier = Modifier,
    @Size(value = 2) colors: IntArray,
    visible: Boolean = true,
) {
    AndroidView(
        modifier = modifier,
        factory = { context ->
            val gradientBackground = GradientDrawable(
                GradientDrawable.Orientation.TOP_BOTTOM,
                colors
            ).apply {
                cornerRadius = 0f
            }
            View(context).apply {
                layoutParams = LayoutParams(
                    LayoutParams.MATCH_PARENT,
                    LayoutParams.MATCH_PARENT
                )
                background = gradientBackground
                visibility = when (visible) {
                    true -> View.VISIBLE
                    else -> View.INVISIBLE
                }
            }
        }
    )
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接