[Разработка под Android, Kotlin] Как сделать цветные тени в Android с градиентом и анимацией
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
На презентации новых макбуков обратил внимание на картинку процессора:
Переливающиеся цветные тени на темном фоне, выглядит классно. Вот дошли руки, решил попробовать нарисовать на андроиде так же. Вот что получилось:
Сразу оговорюсь, что стандартным способом это сделать нельзя, до 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
)
Все. Если это будет кнопкой, нужно применить ripple эффект для foreground вьюхи и так же прописать там отступ, чтобы у нас отображалась анимация нажатия.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование] Вы не знаете деструктуризацию, пока (перевод)
- [Разработка под Android, Тестирование мобильных приложений] Тестируем Android-приложение правильно
- [Графические оболочки, Разработка под Android, Гаджеты, Видеотехника] TV Box или Smart TV?
- [Разработка мобильных приложений, Разработка под Android, Kotlin] Корутинная эволюция в Kotlin. Чем отличаются Channels, Broadcast channels, Shared flows, State flows (перевод)
- [Программирование, Разработка под Android, Kotlin] Koin — библиотека для внедрения зависимостей, написанная на чистом Kotlin (перевод)
- [Разработка под Android] Избегаем поддельных шрифтов в Android
- [Разработка под Android, Учебный процесс в IT, Карьера в IT-индустрии] Я месяц провел в MIT и понял — даже софтверным инженерам не стоит забывать про паяльник
- [Python, Разработка игр, Swift, Kotlin, Дизайн игр] MMORPG больше не в Telegram — Swift и Kotlin — Первый большой проект — Часть 1
- [Delphi, Разработка игр, Unity] Math Invasion. Мой долгострой
- [Разработка мобильных приложений, Разработка под Android, Разработка под MacOS, Разработка под Linux, Разработка под Windows] Разработка мобильных приложений на Python. Создание анимаций в Kivy. Part 2
Теги для поиска: #_razrabotka_pod_android (Разработка под Android), #_kotlin, #_android, #_view, #_teni (тени), #_kotlin, #_shadows, #_frontend, #_razrabotka_pod_android (
Разработка под Android
), #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:41
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
На презентации новых макбуков обратил внимание на картинку процессора: Переливающиеся цветные тени на темном фоне, выглядит классно. Вот дошли руки, решил попробовать нарисовать на андроиде так же. Вот что получилось: Сразу оговорюсь, что стандартным способом это сделать нельзя, до 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 с закругленными углами * Это будет основной цвет нашего контейнера */ private fun createColorDrawable( @ColorInt backgroundColor: Int, cornerRadius: Float ) = GradientDrawable().apply { setColor(backgroundColor) setCornerRadius(cornerRadius) } /**
* Устанавливаем бэкграунд с тенью на вьюху, учитывая 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 ) =========== Источник: habr.com =========== Похожие новости:
Разработка под Android ), #_kotlin |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:41
Часовой пояс: UTC + 5