En la presentación de las nuevas MacBooks, llamé la atención sobre la imagen del procesador:
Las sombras de colores iridiscentes sobre un fondo oscuro se ven geniales.
Así que mis manos se movieron, decidí intentar dibujar en Android de la misma manera. Esto es lo que sucedió:
, , 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
)
Todo. Si se trata de un botón, debe aplicar un efecto dominó al primer plano de la vista y también sangrar allí para que podamos mostrar la animación de clic.