RenderEffect in Android 12
Learn how to use the new RenderEffect API in Android 12 to efficiently add custom styles to your views like blurs, saturation, offset, and more. By Diayan Siat.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
RenderEffect in Android 12
20 mins
- Getting Started
- Adding Effects Before RenderEffect
- Understanding RenderEffect
- Rendering in Android
- Understanding RenderNode
- Understanding the Display List
- Understanding the Canvas
- Adding Some Awesome Effects
- Adding Blur
- Adjusting the Blur
- Adding Color Filter Effects
- Adjusting the Color Filter
- Using the Offset Effect
- Adjusting Offset
- Applying Chain Effects
- Adjusting the Chain Effect
- Applying Fullscreen Blur
- Where to Go From Here?
Adjusting the Blur
You can adjust the blur of a view by varying the values of the radii in the code above. To achieve this, you’ll vary the values of radiusX
and radiusY
. Within your if-else
block, below line 4, add the following code:
binding.blurSlider.addOnChangeListener { _, value, _ ->
//1
if (value != 0f) {
//2
val varyingBlurValue = createBlurEffect(value, value, Shader.TileMode.MIRROR)
//3
binding.imageView.setRenderEffect(varyingBlurValue)
}
}
In the code above:
- Checks that the
slider
value isn’t 0. This prevents passing 0 intocreateBlurEffect
, because it doesn’t take 0 forX
andY
. - Creates an object and stores it in
varyingBlurValue
. - Applies blur to the view.
If the condition is true, you pass in the slider value
for radiusX
and radiusY
.
Build and run. Check Blur and adjust its slider. You’ll see something like this:
Adding Color Filter Effects
RenderEffect also allows you to add different color filter effects. You’ll use the createColorFilterEffect
factory method to saturate/desaturate the photo. To do this, replace the TODO()
under //TODO 2: Add color effect
with:
return RenderEffect.createColorFilterEffect(ColorMatrixColorFilter(
ColorMatrix().apply {
setSaturation(saturation)
}
))
In the code above, you create a ColorMatrix
object and adjust the saturation using the setSaturation
factory method. You then pass the value returned as an argument to ColorMatrixColorFilter
, which is also passed as an argument to createColorFilterEffect
.
If you’d like to know more about the the ColorMatrix
, check out Android’s documentation on it.
Next, replace //TODO 5: Add color filter effect
with the following code to apply this effect to the view:
//1
if (isChecked) {
binding.blurCheck.isClickable = false
binding.offsetEffectsCheck.isClickable = false
binding.chainEffectCheck.isClickable = false
//2
binding.colorFilterSlider.isEnabled = true
//3
val saturation = createSaturationEffect(DEFAULT_SATURATION)
//4
binding.imageView.setRenderEffect(saturation)
} else {
//5
binding.imageView.setRenderEffect(null)
//6
binding.colorFilterSlider.isEnabled = false
binding.blurCheck.isClickable = true
binding.offsetEffectsCheck.isClickable = true
binding.chainEffectCheck.isClickable = true
}
Here’s a breakdown of the code above:
- Determine if
saturationCheckBox
is checked. - If so, enable the saturation slider.
- Create a variable to hold the saturation object.
- Apply saturation to view.
- If
saturationCheckBox
isn’t checked, remove the filter from the view by setting RenderEffect tonull
. - Disable the saturation slider.
Build and run. Check Saturation, and you’ll see something like this:
Adjusting the Color Filter
You can adjust the color effect of a view by varying the saturation value of setSaturation
in the code above. In the code you just added, add the following code below line 4 within the if
statement.
binding.colorFilterSlider.addOnChangeListener { _, value, _ ->
val varyingSaturationValue = createSaturationEffect(value)
binding.imageView.setRenderEffect(varyingSaturationValue)
}
Build and run. Check Saturation and adjust the saturation slider. You’ll see something like this:
setSaturation
maps the color of the view to grayscale, and 1 maps it to the original color. Adjusting the value above 1 increases the saturation.
Using the Offset Effect
Sometimes, all you need is to be able to offset a view. RenderEffect provides the createOffsetEffect
static method, which takes in two arguments: X
and Y
. These offset the drawing content in the horizontal and vertical planes, respectively. To implement an offset, replace the TODO()
under //TODO 3: Add offset effect
with:
return RenderEffect.createOffsetEffect(offsetX, offsetY)
Next, apply offset when offsetCheckbox
is checked by replacing //TODO 6: Add offset effect
with:
if (isChecked) {
binding.blurCheck.isClickable = false
binding.saturationCheck.isClickable = false
binding.chainEffectCheck.isClickable = false
//1
binding.offsetSlider.isEnabled = true
//2
val offsetEffect = createOffsetEffect(DEFAULT_OFFSET, DEFAULT_OFFSET)
//3
binding.imageView.setRenderEffect(offsetEffect)
} else {
//4
binding.imageView.setRenderEffect(null)
//5
binding.offsetSlider.isEnabled = false
binding.blurCheck.isClickable = true
binding.saturationCheck.isClickable = true
binding.chainEffectCheck.isClickable = true
}
What you just did is in every way similar to the first two you did earlier.
Build and run. Check Offset, and you’ll see something like this:
Adjusting Offset
You can adjust the offset of a view by varying the values of offsetX
and offsetY
in the code above. To achieve this, pass in the slider value as the user adjusts it. Place the following code below line 3 within the if
block:
binding.offsetSlider.addOnChangeListener { _, value, _ ->
val varyingOffsetValue = createOffsetEffect(value, value)
binding.imageView.setRenderEffect(varyingOffsetValue)
}
Build and run. Check Offset and adjust the offset effects slider. You’ll see the following:
Applying Chain Effects
The RenderEffect API gives you the ability to combine and apply multiple effects to Views, known as the chain effect. You’ll add a chain effect by applying two effects to the dog image: Blur and Saturation. Replace //TODO 7: Add chain effect
with:
//1
if (blur < 1) {
//2
return
} else {
//3
val blurry = createBlurEffect(blur, blur, Shader.TileMode.MIRROR)
//4
val saturate = createSaturationEffect(saturation)
//5
val chainEffect = RenderEffect.createChainEffect(blurry, saturate)
//6
binding.imageView.setRenderEffect(chainEffect)
}
Here's a breakdown of the code above:
- Checks if
blur
is less than 1. If it is, exit the code. - Creates a blur object.
- Creates a saturation object.
- Produces a chain effect object by implementing the factory method
createChainEffect
. This takes in two RenderEffect objects as arguments. - Applies the chain effect to the view.
- Removes the chain effect from the view if the checkboxes are unchecked.
Now, to apply chain effect to the view, replace //TODO 8: Add chain effect
with the code below:
//1
if (isChecked) {
binding.blurCheck.isClickable = false
binding.saturationCheck.isClickable = false
binding.offsetEffectsCheck.isClickable = false
//2
binding.chainEffectSlider.isEnabled = true
//3
applyChainEffect(DEFAULT_BLUR, DEFAULT_SATURATION)
} else {
//4
binding.imageView.setRenderEffect(null)
//5
binding.chainEffectSlider.isEnabled = false
binding.blurCheck.isClickable = true
binding.saturationCheck.isClickable = true
binding.offsetEffectsCheck.isClickable = true
}
Here's a walkthrough of the code above:
- Determines if
chainEffectCheckbox
is checked. - Enables chain effect slider by setting it to
true
. - Applies chain effect by passing in default values to
applyChainEffect
. - Removes effect from view by passing
null
tosetRenderEffect
. - Disables chain effect slider if Chain Effect is unchecked.
Build and run. Check Chain Effect, and you'll see something like this:
createChainEffect
could produce slightly different results on the view. Try changing the order of arguments, and compare the results.
Adjusting the Chain Effect
You can adjust the chain effect of a view by varying the effects passed into createChainEffect
. To achieve this, vary the blur
and saturation
values by moving the slider. Add the following code below line 3 within the if
block:
binding.chainEffectSlider.addOnChangeListener { _, value, _ ->
applyChainEffect(value, value)
}