At the presentation of new MacBooks, I drew attention to the processor picture:
Iridescent colored shadows on a dark background looks cool.
So my hands got around, I decided to try to draw on android the same way. Here's what happened:
, , api 28 elevation, api 28 , . drawable, background padding , .
Drawable :
/**
* drawable -
*/
private fun createShadowDrawable(
@ColorInt colors: IntArray,
cornerRadius: Float,
elevation: Float,
centerX: Float,
centerY: Float
): ShapeDrawable {
val shadowDrawable = ShapeDrawable()
//
shadowDrawable.paint.setShadowLayer(
elevation, //
0f, //
0f, //
Color.BLACK //
)
/**
*
*
* @param centerX - SweepGradient .
* @param centerY -
* @param colors - . ,
*
* @param position - 0 1.
* null ..
*/
shadowDrawable.paint.shader = SweepGradient(
centerX,
centerY,
colors,
null
)
//
val outerRadius = FloatArray(8) { cornerRadius }
shadowDrawable.shape = RoundRectShape(outerRadius, null, null)
return shadowDrawable
}
drawable , , . drawable:
/**
* drawable
*
*/
private fun createColorDrawable(
@ColorInt backgroundColor: Int,
cornerRadius: Float
) = GradientDrawable().apply {
setColor(backgroundColor)
setCornerRadius(cornerRadius)
}
-. LayerDrawable . 1 - , 2 - .
/**
* , padding
*/
private fun View.setColorShadowBackground(
shadowDrawable: ShapeDrawable,
colorDrawable: Drawable,
padding: Int
) {
val drawable = LayerDrawable(arrayOf(shadowDrawable, colorDrawable))
drawable.setLayerInset(0, padding, padding, padding, padding)
drawable.setLayerInset(1, padding, padding, padding, padding)
setPadding(padding, padding, padding, padding)
background = drawable
}
:
//
targetView.doOnNextLayout {
val colors = intArrayOf(
Color.WHITE,
Color.RED,
Color.WHITE
)
val cornerRadius = 16f.dp
val padding = 30.dp
val centerX = it.width.toFloat() / 2 - padding
val centerY = it.height.toFloat() / 2 - padding
val shadowDrawable = createShadowDrawable(
colors = colors,
cornerRadius = cornerRadius,
elevation = padding / 2f,
centerX = centerX,
centerY = centerY
)
val colorDrawable = createColorDrawable(
backgroundColor = Color.DKGRAY,
cornerRadius = cornerRadius
)
it.setColorShadowBackground(
shadowDrawable = shadowDrawable,
colorDrawable = colorDrawable,
padding = 30.dp
)
}
. .
/**
* drawable-
*/
private fun animateShadow(
shapeDrawable: ShapeDrawable,
@ColorInt startColors: IntArray,
@ColorInt endColors: IntArray,
duration: Long,
centerX: Float,
centerY: Float
) {
/**
* 0f 1f
* [ColorUtils.blendARGB]
*/
ValueAnimator.ofFloat(0f, 1f).apply {
// . ,
val invalidateDelay = 100
var deltaTime = System.currentTimeMillis()
//
val mixedColors = IntArray(startColors.size)
addUpdateListener { animation ->
if (System.currentTimeMillis() - deltaTime > invalidateDelay) {
val animatedFraction = animation.animatedValue as Float
deltaTime = System.currentTimeMillis()
//
for (i in 0..mixedColors.lastIndex) {
mixedColors[i] = ColorUtils.blendARGB(startColors[i], endColors[i], animatedFraction)
}
//
shapeDrawable.paint.shader = SweepGradient(
centerX,
centerY,
mixedColors,
null
)
shapeDrawable.invalidateSelf()
}
}
repeatMode = ValueAnimator.REVERSE
repeatCount = Animation.INFINITE
setDuration(duration)
start()
}
}
:
// . .
val endColors = intArrayOf(
Color.RED,
Color.WHITE,
Color.RED
)
animateShadow(
shapeDrawable = shadowDrawable,
startColors = colors,
endColors = endColors,
duration = 2000,
centerX = centerX,
centerY = centerY
)
Everything. If this is a button, you need to apply a ripple effect to the foreground of the view and also indent there so that we can display the click animation.