Русский
Русский
English
Статистика
Реклама

Render effect

То, чего нам так не хватало Render Effect в Android 12

21.05.2021 10:23:30 | Автор: admin

Иногда бывает нужно размыть задний план на экранах мобильного приложения, например в чате. Теперь это можно сделать всего парой строк кода. В Android 12 появился новый API Render Effect, который позволяет накладывать визуальные эффекты на Canvas или View. Этот API радует своей простотой и высокой скоростью отрисовки. Наибольший интерес представляет Render Effect дляразмытия (BlurEffect), но в этой статье мы затронем и остальные виды эффектов. Материал может быть полезен не только андроид-разработчикам, но и дизайнерам мобильных приложений.

Итак, каким образом можно размыть задний план при отображении диалога? Раньше в Андроиде для этого надо было отрисовать все вью с заднего плана на bitmap-е и затем размыть его с помощью RenderScript или OpenGL. Но это означает, что в проекте появится немало запутанного для прочтения кода, либо придется подключить стороннюю библиотеку для размытия. Плюс добавятся обработчики событий отрисовки и код для получения битмапа. К тому же по производительности эти решения могут не давать желаемого результата. При использовании некоторых популярных библиотек для размытия разработчики замечают лаги, например, если есть RecyclerView, который содержит много BlurView.

С помощью Render Effect мы можем всего парой строк кода реализовать блюр без лагов. Render Effect работает эффективно за счет того, что он использует Render Nodes (узлы отрисовки). Они образуют иерархию аппаратно ускоренной отрисовки, и эта отрисовка происходит с помощью GPU (графического процессора). Перерисовываться будут только те части UI, которые оказались в состоянии invalidated. Все вычисления для Render Effect выполняются в Render потоке и не блокируют UI.

У Render Effect очень простой API:

val renderNode = RenderNode("myRenderNode")renderNode.setPosition(0, 0, 50, 50)renderNode.setRenderEffect(renderEffect) canvas.drawRenderNode(renderNode)

Мы добавили эффект для RenderNode методом setRenderEffect(), и его отрисовка происходит в методе Canvas.drawRenderNode().

Можно применить Render Effect и к узлу отрисовки вьюшки:

imageView.setRenderEffect(renderEffect)

Приведем пример создания эффекта:

val blurEffect = RenderEffect.createBlurEffect(       20f, //radiusX       20f, //radiusY              Shader.TileMode.CLAMP)imageView.setRenderEffect(blurEffect)

Здесь мы создаем эффект размытия и применяем его к узлу отрисовки, привязанному к imageView. Задаем интенсивность размытия по оси X и оси Y (Значения 20f) . Последний аргумент Shader.TileMode определяет то, как будет выглядеть эффект на краях отрисовываемой области.

Если применить размытие к корневому layout-у, то будут размыты все вьюшки: и кнопка, и ползунки.

Варианты значений TileMode:

  • MIRROR. Используются зеркальные отражения изображения по вертикали и горизонтали, при этом на границах нет резких переходов.

  • CLAMP. Если shader выходит за пределы границ, используется цвет пикселей на границе. Выглядит практически также как MIRROR, но возможно незначительное искажение формы объектов возле границы.

  • REPEAT. Изображение повторяется горизонтально и вертикально. На изображении заметно, как темная вода из нижней части изображения отразилась на верхней границе изображения.

  • DECAL (появился в API 31). Отрисовка shader-а только в пределах границ. Можно заметить, что на границе изображение стало чуть светлее.

Мы также можем применить размытие при создании тени. Конечно, можно просто создать тень с помощью свойства Elevation:

android:elevation="10dp"

Однако, в этом случае мы не сможем задавать направление, в котором должна отбрасываться тень, или ее цвет. Также тень для Elevation по умолчанию работает только с формами скругленного прямоугольника (круг и прямоугольник частные случаи прямоугольника со скругленными углами). Для тени другой формы нужно реализовать ViewOutlineProvider, чтобы он возвращал Outline с setPath(...), а это может быть весьма трудоемко.

Кастомную тень можно создать и с помощью xml, прописав <shape> с градиентом:

<gradient       android:type="radial"       android:centerColor="#90000000"       android:gradientRadius="70dp"       android:startColor="@android:color/white"       android:endColor="@android:color/transparent"/>

Мы получили тень круглой формы, ложащуюся от белого круга одинаково во всех направлениях то есть сверху она такой же длины, что и снизу. Но мы все же не сможем таким способом сделать тень сложной формы.

Сначала для сравнения покажем, как можно реализовать аналогичную тень круглой формы с помощью RenderEffect. Создаем круг серого цвета:

<shape android:shape="oval">   <solid android:color="#CCCCCC" /></shape>

Затем этот drawable устанавливаем в качестве содержимого ImageView и применяем размытие к ImageView:

imageView.setImageResource(R.drawable.gray_circle)val renderEffect = RenderEffect.createBlurEffect(20f, 20f, Shader.TileMode.CLAMP)imageView.setRenderEffect(renderEffect)

А вот как с помощью RenderEffect можно добиться тени произвольной формы и, если понадобится, произвольного цвета:

<ImageView   android:id="@+id/shadow"   android:layout_width="150dp"   android:layout_height="150dp"   android:src="@drawable/ic_baseline_time_to_leave_24"   android:tint="#444444"/>
val effect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP)imageViewfindViewById<ImageView>(R.id.shadow).setRenderEffect(effect)

Если попробовать сделать размытие с разными значениями радиуса, например, 20 по горизонтали и 0 по вертикали, то получится эффект, похожий на смазанный снимок.

Всего есть семь видов RenderEffect:

  • Bitmap отрисовывает содержимое битмапа. Обычно используется в цепочке для наложения последующих эффектов.

  • Blur размытие по оси X и Y.

  • ColorFilter применяется цветной фильтр (см. скриншот ниже).

  • Offset сдвиг по осям X, Y (жаль, что нет поворота).

  • Shader отрисовывает шейдер, переданный в аргументах. С помощью шейдеров можно создавать, например, различные градиенты.

  • Chain комбинация 2 эффектов. Результат отрисовки одного эффекта используется как источник для второго эффекта.

  • BlendMode для объединения 2 эффектов в определенном режиме.

Можно добиться красивого эффекта замерзшего стекла (или запотевшего окна): он достигается сочетанием размытия и наложения прозрачно-белого цвета.

Применим такой эффект к панели внизу экрана. Фактически здесь надо сочетать три эффекта: отрисовка битмапа + размытие + цветной фильтр. К сожалению, нельзя сделать так, чтобы размывалась та часть UI, которая отрисована под вьюшкой. Поэтому RenderEffect нужно применить не к самой панели, а к тому, что находится под ней и содержит фоновое изображение: к imageView или к корневому layout-у. Комбинировать эффекты можно 2 способами: передавать один эффект в параметр create-метода другого эффекта, либо использовать метод createChainEffect():

val bmpEffect = RenderEffect.createBitmapEffect(...)val blurBmpEffect = RenderEffect.createBlurEffect(..., bmpEffect, ..)val finalEffect = RenderEffect.createColorFilterEffect(..., blurBitmapEffect)

или

val finalEffect = RenderEffect.createChainEffect(colorEffect,blurEffect)

Приведем пример наложения цветного фильтра:

val argb = Color.valueOf(red, green, blue, transparency).toArgb()val colorEffect = RenderEffect.createColorFilterEffect(       PorterDuffColorFilter(argb, PorterDuff.Mode.SRC_ATOP))

Результат:

Или можно с помощью цветного фильтра поменять насыщенность изображения:

val matrix = ColorMatrix()matrix.setSaturation(0f)imageView.setRenderEffect(RenderEffect.createColorFilterEffect(ColorMatrixColorFilter(matrix)))

Результат для setSaturation(0f) и setSaturation(100f):

BlendMode

С помощью метода RenderEffect.createBlendModeEffect() можно объединить два эффекта в одном из режимов:

  • CLEAR

  • SRC

  • DST

  • SRC_OVER

  • DST_OVER

  • SRC_IN

  • DST_IN

  • SRC_OUT

  • DST_OUT

  • SRC_ATOP

  • DST_ATOP

  • XOR

  • PLUS

  • MODULATE

  • SCREEN

  • OVERLAY

  • DARKEN

  • LIGHTEN

  • COLOR_DODGE

  • COLOR_BURN

  • HARD_LIGHT

  • SOFT_LIGHT

  • DIFFERENCE

  • EXCLUSION

  • MULTIPLY

  • HUE

  • SATURATION

  • COLOR

  • LUMINOSITY

Подробное описание режимов можно посмотреть здесь. Для примера приведем три режима (здесь источник, то есть первый RenderEffect это синий квадрат, а приемник, то есть второй Render Effect красный круг):

DST_ATOP выбрасывает пиксели, которые не накладываются на источник. DST_ATOP выбрасывает пиксели, которые не накладываются на источник. OVERLAY умножает цвета.OVERLAY умножает цвета.COLOR сохраняет оттенок и насыщенность источника, а яркость делает как у приемника.COLOR сохраняет оттенок и насыщенность источника, а яркость делает как у приемника.

Как уже упоминалось выше, RenderEffect работает крайне эффективно. При скролле RecyclerView с большим количеством элементов, у которых размыто по две ImageView (размытие с параметрами radiusX: 20f, radiusY: 20f, Shader.TileMode.CLAMP), никаких подтормаживаний не наблюдается. Размытие вью с анимацией тоже работает без задержек. В настоящий момент работа RenderEffect была проверена автором на эмуляторе Android 12 (API 31).

Что касается поддержки RenderEffect API, можно надеяться, что она будет добавлена в библиотеке AndroidX для Android 10 и 11, так как Render Nodes, лежащие в основе RenderEffect, были добавлены в Android 10 (API level 29). Однако, как пишут в официальной документации, Render Effect могут поддерживать не все устройства: Different Android devices may or may not support the feature due to limited processing power.

Отметим также, что Render Effect является одним из вариантов замены для RenderScript API, который устарел начиная с Android 12.

Заключение

Итак, в Android 12 мы получили то, чего так долго не хватало многим разработчикам простой API для отрисовки визуальных эффектов. Сочетая простые эффекты (размытие, цветные фильтры, шейдеры), мы можем получать интересные графические результаты. При этом больше не нужно возиться с вытаскиванием bitmap-изображения, эффект можно просто повесить на вьюшку. А благодаря использованию RenderNodes, отрисовка RenderEffect работает очень быстро.

Спасибо за внимание! Надеемся, что наш опыт был вам полезен.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru