MaterialFrame leveled up with a Blur theme!

Get it from Github and Nuget:

https://github.com/roubachof/Sharpnado.MaterialFrame

Sharpnado.MaterialFrame gets its own github repository to showcase all its theming power:

  • Blur
  • Acrylic
  • Dark
  • Light

The Blur theme supports 3 different blur styles based on iOS UIVisualEffectView:

  • Light
  • Dark
  • ExtraLight

3 blur styles

On Android it has advanced blur customization:

  • AndroidBlurOverlayColor
  • AndroidBlurRadius
  • AndroidBlurRootElement (Performance)

Read the doc on https://github.com/roubachof/Sharpnado.MaterialFrame.

Hey man this is not Acrylic!

James tweeted about its ongoing Animal Crossing turnip stock options pricing app (sorry I'm a dota 2 player :):

Then someone replied with the most incredible outrageous message.

tweet

Well he was totally right :)

tweet answer

Acrylic effect is a blur effect with some kind of opacity, and it was next on my list for the MaterialFrame.

Dynamic theming in the Silly! app

Implementation journey

For iOS, the implementation was really easy: it's just supported by the UIVisualEffectView.
I just had to add this kind of view as a sub view of my native iOS UIView and it was it :)

For Android, well, it was a hell of a journey.

I did my research, and found two solid candidates:

For the first one, there even was a xamarin binding:

Since my android renderer is implemented with a CardView (which is a FrameLayout), I thought BlurView was the closest to it (it also inherits from FrameLayout).

Since the implementation was really small, I just converted the java code to C# code. It removes the reference to another nuget package, and you can really adjust it to your needs.

After a lot of testing, turned out that it was less usable and solid that I thought.
First it works badly if you just use it in a UI hierarchy behind another view, it kinds of blur together the background elements and some front elements...

Also the rendering was sometimes flickering.

So I just disabled the BlurView implementation and traded it with the RealtimeBlurView.

The implementation for the RealtimeBlurView is simpler and made by a solid member of the Android community.

Tu Yimin

It went smoothly till I stumbled upon a Xamarin.Android bug:

https://github.com/xamarin/xamarin-android/issues/4548

Causing SIGSEV while debugging.

To overcome this issue I created a special Android configuration property:

AndroidMaterialFrameRenderer.ThrowStopExceptionOnDraw => If set to true, the rendering result could be better (clearer blur not mixing front elements). However due to a bug in the Xamarin framework https://github.com/xamarin/xamarin-android/issues/4548, debugging is impossible with this mode (causes SIGSEGV).
My suggestion would be to set it to false for debug, and to true for releases.

If you're not debugging the app won't crash but annoying UNHANDLED EXCEPTION will be spammed in the logs.
So I created another issue for this:

https://github.com/xamarin/xamarin-android/issues/4632

It is a weird error caused by an Exception thrown in java runtime and caught in C# runtime:

public class StopException : Exception {} // same result if RuntimeException

public class RealtimeBlurView : View  
{
    ....

    public override void Draw(Canvas canvas)
    {
        if (mIsRendering)
        {
            if (AndroidMaterialFrameRenderer.ThrowStopExceptionOnDraw)
                // Quit here, don't draw views above me            
                throw STOP_EXCEPTION;

            return;
        }

        if (RENDERING_COUNT > 0)
        {
            // Doesn't support blurview overlap on another blurview
        }
        else
        {
            base.Draw(canvas);
        }
    }
}

... 

class PreDrawCallback : ViewTreeObserver.IOnPreDrawListener  
{
    public bool OnPreDraw()
    {

        try
        {
            ...

            decor.Draw(blurView.mBlurringCanvas); 
            // |=>  throws custom StopException since BlurView is an indirect child of decor
            //    |=>  monodroid is logging "UNHANDLED EXCEPTION + StopException <stack trace>"
        }
        catch(Exception e)
        {
            // the code here is executed, so the exception, is handled
            // even if monodroid logs the unhandled exception
        }
    }
}

Anyway, this was an issue for me, but it won't be one for the users of the MaterialFrame, all those scenarios are taken care of with the ThrowStopExceptionOnDraw android property.

As said, just set it to to false during debugging sessions, and to true in RELEASE (or DEBUG without debugger attached).

As written above, if the property is set to false, the draw method will just return without interrupting the draw chain (no StopException is thrown). So elements in front of the blurred view will also be mixed in the blur.