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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

  1. Checks that the slider value isn’t 0. This prevents passing 0 into createBlurEffect, because it doesn’t take 0 for X and Y.
  2. Creates an object and stores it in varyingBlurValue.
  3. 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:

Animation of Blur box being checked and the slider moving with the dog picture varying in blurriness

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:

  1. Determine if saturationCheckBox is checked.
  2. If so, enable the saturation slider.
  3. Create a variable to hold the saturation object.
  4. Apply saturation to view.
  5. If saturationCheckBox isn’t checked, remove the filter from the view by setting RenderEffect to null.
  6. Disable the saturation slider.

Build and run. Check Saturation, and you’ll see something like this:

Instafilter with Saturation checked and the dog image has much more vivid colors

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:

Animation of Saturation box being checked and slider moving with color saturation changing in the dog image

Note: Passing a value of 0 to 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:

Instafilter with dog photo slightly shifted

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:

Animation of Offset being checked and the slider bar moving with the dog photo moving downward and to the right

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:

  1. Checks if blur is less than 1. If it is, exit the code.
  2. Creates a blur object.
  3. Creates a saturation object.
  4. Produces a chain effect object by implementing the factory method createChainEffect. This takes in two RenderEffect objects as arguments.
  5. Applies the chain effect to the view.
  6. 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:

  1. Determines if chainEffectCheckbox is checked.
  2. Enables chain effect slider by setting it to true.
  3. Applies chain effect by passing in default values to applyChainEffect.
  4. Removes effect from view by passing null to setRenderEffect.
  5. Disables chain effect slider if Chain Effect is unchecked.

Build and run. Check Chain Effect, and you'll see something like this:

Instafilter with dog image blurred and saturated with color

Note: The order in which arguments are passed into 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)
}

Animation of Chain Effect being checked and the slider adjusted with the dog photo getting blurrier and more color saturated