简体   繁体   中英

Jetpack Compose inner shadow

How to create an inner shadow with Jetpack Compose? The gradient should run from black on the outside to transparent on the inside. Modifier.shadow() is just for outer shadows. Using negative elevation is not working.

在此处输入图像描述

fun Modifier.innerShadow(
    color: Color = Color.Black,
    cornersRadius: Dp = 0.dp,
    spread: Dp = 0.dp,
    blur: Dp = 0.dp,
    offsetY: Dp = 0.dp,
    offsetX: Dp = 0.dp
) = drawWithContent {

    drawContent()

    val rect = Rect(Offset.Zero, size)
    val paint = Paint()

    drawIntoCanvas {

        paint.color = color
        paint.isAntiAlias = true
        it.saveLayer(rect, paint)
        it.drawRoundRect(
            left = rect.left,
            top = rect.top,
            right = rect.right,
            bottom = rect.bottom,
            cornersRadius.toPx(),
            cornersRadius.toPx(),
            paint
         )
         val frameworkPaint = paint.asFrameworkPaint()
         frameworkPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
         if (blur.toPx() > 0) {
             frameworkPaint.maskFilter = BlurMaskFilter(blur.toPx(), BlurMaskFilter.Blur.NORMAL)
         }
         val left = if (offsetX > 0.dp) {
             rect.left + offsetX.toPx()
         } else {
             rect.left
         }
         val top = if (offsetY > 0.dp) {
             rect.top + offsetY.toPx()
         } else {
             rect.top
         }
         val right = if (offsetX < 0.dp) {
             rect.right + offsetX.toPx()
         } else {
             rect.right
         }
         val bottom = if (offsetY < 0.dp) {
             rect.bottom + offsetY.toPx()
         } else {
             rect.bottom
         }
         paint.color = Color.Black
         it.drawRoundRect(
             left = left + spread.toPx() / 2,
             top = top + spread.toPx() / 2,
             right = right - spread.toPx() / 2,
             bottom = bottom - spread.toPx() / 2,
             cornersRadius.toPx(),
             cornersRadius.toPx(),
             paint
         )
         frameworkPaint.xfermode = null
         frameworkPaint.maskFilter = null
    }
}

Usage:

Box(
    modifier = Modifier
        .width(240.dp)
        .height(180.dp)
        .outerShadow(
            color = Color(0xff000000),
            alpha = 0.5f,
            cornersRadius = 20.dp,
            shadowBlurRadius = 30.dp,
            offsetX = 0.dp,
            offsetY = 15.dp
        )
        .clip(RoundedCornerShape(20.dp))
        .background(Color(0xFF282A2F))
        .innerShadow(
            blur = 1.dp,
            color = Color(0xff00FFFF),
            cornersRadius = 20.dp,
            offsetX = (-40.5).dp,
            offsetY = (-10.5).dp
        )
        .innerShadow(
            blur = 20.dp,
            color = Color(0xffff0000),
            cornersRadius = 20.dp,
            offsetX = 0.5.dp,
            offsetY = 0.5.dp
        )
        .padding(14.dp),
    contentAlignment = Alignment.Center
) {
    Text(
        text = stringResource(id = R.string.notification_service),
        modifier = Modifier,
        color = Color.White
     )
}

I hope it helps. outerShadow is custom modifier too, copied from other websites.

Position of Modifier.shadow relative to Modifier.background places shadow inside your component. Also you can create Modifier.drawWithContent and frameworkPaint a show with blur.

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(20.dp)
) {
    ComponentWithInnerShadow()
    Spacer(modifier = Modifier.height(12.dp))
    ComponentWithOuterShadow()
    Spacer(modifier = Modifier.height(12.dp))
    ComponentWithCustomInnerShadow()
}

@Composable private fun ComponentWithInnerShadow() {
    Column(
        modifier = Modifier
            .clip(RoundedCornerShape(5.dp))
            .background(Color.Yellow)
            .shadow(2.dp, shape = RoundedCornerShape(5.dp))
    ) {
        Text(text = "Hello World", modifier = Modifier.padding(12.dp))
    } }

@Composable
private fun ComponentWithOuterShadow() {
    Column(
        modifier = Modifier
            .shadow(2.dp, shape = RoundedCornerShape(5.dp))
            .background(Color.Yellow)

    ) {
        Text(text = "Hello World", modifier = Modifier.padding(12.dp))
    }
}

@Composable
private  fun ComponentWithCustomInnerShadow() {
    Column(
        modifier = Modifier.innerShadow()

    ) {
        Text(text = "Hello World", modifier = Modifier.padding(12.dp))
    }
}

And with composed modifier. I didn't set parameters just set arbitrary numbers you can set your own parameters, and set color if you want to. This one looks better than standard inner shadow though. You need to provide color for foreground and shape with this one either. I set color and drew Rounded rectangle for demonstration

fun Modifier.innerShadow() = composed(
    inspectorInfo = {

    },
    factory = {

        val paint = remember() {
            Paint()
        }

        val foregroundPaint = remember() {
            Paint().apply {
                color = Color.Yellow
            }
        }

        val frameworkPaint = remember {
            paint.asFrameworkPaint()
        }

        Modifier.drawWithContent {
            this.drawIntoCanvas {
                val color = Color.LightGray

                val radius = 2.dp.toPx()

                val shadowColor = color
                    .copy(alpha = .7f)
                    .toArgb()
                val transparent = color
                    .copy(alpha = 0f)
                    .toArgb()

                frameworkPaint.color = transparent

                frameworkPaint.setShadowLayer(
                    radius,
                    0f,
                    0f,
                    shadowColor
                )
                val shadowRadius = 4.dp.toPx()

                it.drawRoundRect(
                    left = 0f,
                    top = 0f,
                    right = this.size.width,
                    bottom = this.size.height,
                    radiusX = 5.dp.toPx(),
                    radiusY = 5.dp.toPx(),
                    paint = foregroundPaint
                )

                it.drawRoundRect(
                    left = 0f,
                    top = 0f,
                    right = this.size.width,
                    bottom = this.size.height,
                    radiusX = 5.dp.toPx(),
                    radiusY = 5.dp.toPx(),
                    paint = paint
                )

                it.drawRoundRect(
                    left = shadowRadius,
                    top = shadowRadius,
                    right = this.size.width - shadowRadius,
                    bottom = this.size.height - shadowRadius,
                    radiusX = 5.dp.toPx(),
                    radiusY = 5.dp.toPx(),
                    paint = foregroundPaint
                )
                drawContent()

            }
        }
    }
)

在此处输入图像描述

This is not supported but you have alternative options:

  1. Use a border modifier with a gradient brush
  2. Use Android Canvas
  3. Use a gradient and Modifier.drawBehind to draw the inset shadow underneath the content

Source

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM