Lors de la présentation des nouveaux MacBook, j'ai attiré l'attention sur l'image du processeur:
Les ombres colorées irisées sur un fond sombre ont l'air cool.
Alors mes mains se sont déplacées, j'ai décidé d'essayer de dessiner sur Android de la même manière. Voici ce qui s'est passé:
, , 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
)
Tout. S'il s'agit d'un bouton, vous devez appliquer un effet d'entraînement au premier plan de la vue et y mettre en retrait afin que nous puissions afficher l'animation de clic.