How to Create a Dynamic Blur Effect on Android

Intersog
3 min readJul 21, 2016

--

Although there’s abundance of information about how to blur an image on Android, the question remains how to effectively and laglessly redraw a blurred bitmap as the content gets modified (like in iOS).

So, my project objectives were:

  • Create ViewGroup that is able to blur content it overlays and display it as a background image. I’ll call my project BlurView
  • Add any Views to this ViewGroup
  • Independence from any particular implementation of parent ViewGroup
  • Redraw blurred background each time our View content changes
  • Full cycle blurring and rendering in less than 16 ms
  • Blurring strategy should be modifiable

In this article I’m going to address the key points only, so check out Github for details.

Sample gif:

How to create bitmap with BlurView content?

Let’s create Canvas and then define the bitmap upon which drawing will actually be performed. Let’s push the entire View Hierarchy to this Canvas.

All Views featured on the screen are now drawn in internalBitmap.

Advice:

If your rootView has no pre-set background, you should first draw Activity background on the Canvas, otherwise it’ll be transparent in bitmap.

Now I want to draw only part of hierarchy I need, i.e. under my BlurView and with appropriate dimension. Besides, I’d like to resize the bitmap upon which drawing will be performed. Let’s add scaleFactor, it’s suggested that coefficient equals the power of two.

Reduce the bitmap 8 times and set up Canvas matrix so that we can draw on it correctly with consideration of position, size and scale factor.

No when we launch rootView.draw(internalCanvas), we’ll have a reduced copy of the entire View hierarchy on the internalBitmap.

Advice: to prevent BlurView from self-blurring, you can redefine the draw(Canvas canvas) method in BlurView and check which canvas it’s requested to be drawn upon. If it’s a system canvas — permit drawing, if it’s internalCanvas — prohibit drawing.

Blur, actually…

I built BlurAlgorithm interface and added StackBlur library (by Mario Klingemann) and RenderScriptBlur. RenderScript is deployed to GPU. To save memory, I register result in the same input bitmap. That’s optional, however. You can also try to cache outAllocation and re-use it if the input image hasn’t changed.

When to redraw blur?

Several options are possible:

  1. Update manually when you know for sure the content will change
  2. Tie to scroll events if BlurView is over a scrollable container
  3. Add a listener to ViewTreeObserver

I opted for #3 and used OnPreDrawListener. It should be noted that its OnPreDraw method is pulled each time any View in hierarchy draws itself. Yes, you’ll have to redraw BlurView hell many times, but it does ensure automation and independence from a programmer.

Advice: onPreDraw will be called when the entire hierarchy is drawn on internalCanvas as well as at BlurView rendering. To avoid this issue, I created flag isMeDrawingNow.

Conclusion

The entire drawing cycle takes 1–4 ms on Nexus 4 and Nexus 5, about 10 ms on Galaxy Nexus, 8–16 ms on Sony Xperia Z3. I should test it on more devices though. In general, I’m very satisfied with the performance.

And what’s your take on this?

Original article by Intersog’s Android Developer is available here (in RU).

--

--

Intersog

Chicago-based provider of full-cycle custom software engineering and IT staffing solutions with own R&D Centers in North America and Europe.