Xamarin.Tips – Overriding Android Button Shadows/Elevation

Since Material Design’s implementation in the Android OS, some controls that ship with either the new styles, or with the App Compat packages place some under-the-cover restrictions on what you can do with the control by default. In this example, we will look at updating the App Compat Button Shadows and Elevation that ship with the control.

According to Material Design’s standards, “raised buttons” (versus flat buttons and floating action buttons) should have a resting elevation of 2dp, and an pressed/hover elevation of 8dp.

whatismaterial_3d_elevation_component02

This principle is also implemented in the App Compat Button. However, if you try to update the Elevation of your Button, you’ll notice that it won’t stay that way on the redraw, but will go right back to the 4dp it is by default.


supportButton.Elevation = 9; // set it directly
ViewCompat.SetElevation(supportButton, 9); // set using app compat method

...

Console.WriteLine(supportButton.Elevation); // will return 4...

So why is this? And how is Android creating the pressed animation automatically to increase the elevation? It certainly isn’t any code we’ve written. The answer is in the StateListAnimator property of the Button. The StateListAnimator is responsible for setting properties of the Button during certain states such as Enabled, Disabled, Focused, Pressed, etc. and is what is overriding the manual set of Button.Elevation.

You can override this in a few different ways to claim back full control. First, if you want to handle your different different states manually in your code, you can set the StateListAnimator to a new instance, or null, then set the Elevation to what you want.

In Code

supportButton.StateListAnimator = new StateListAnimator();
ViewCompat.SetElevation(supportButton, 9);

...

Console.WriteLine(supportButton.Elevation); // 9!

The most reusable way to do this is to subclass Button and set the StateListAnimator in the constructor:

CustomElevatingButton.cs

public class CustomElevatingButton : Android.Support.V7.Widget.AppCompatButton
{
    public CustomElevatingButton(Context context): base(context)
    {
        StateListAnimator = new StateListAnimator();
    }
}

Using Styles

Alternatively, you can set it using styles for your Button:

styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:buttonStyle">@style/NoShadowButton</item>
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@null</item>
    </style>
</resources>

You can also do it per-button:

 styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        ...
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@null</item>
    </style>
</resources>

some_layout.axml

...
<Button style="@style/NoShadowButton" ... />
...

In Xamarin.Forms

We can do the same thing in Xamarin.Forms with either a custom renderer or a custom Effect. In this example, we will create a universal Xamarin.Forms.Button custom renderer to set an explicit height:

ElevatedButtonRenderer

public class ElevatedButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
    public override void OnElementChanged(ElementChangedEventArgs<Button> e)
    {
        StateListAnimator = null; // clear the state list animator
        Elevation = 9; // set the elevation
    }
}

Creating Your Own StateListAnimator

Of course, instead of clearing the StateListAnimator and handling your elevation manually, you could create your own to handle the states and animations however you want. Google has documentation included in the discussion about animations here. Here’s an example of creating and applying your own:

anim/reverse_state_list_animator.xml

<!-- animate the elevation property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_pressed="true">
    <set>
      <objectAnimator android:propertyName="elevation"
        android:duration="@android:integer/config_shortAnimTime"
        android:valueTo="0dp"
        android:valueType="floatType"/>
        <!-- you could have other objectAnimator elements
             here for "x" and "y", or other properties -->
    </set>
  </item>
  <item android:state_enabled="true"
    android:state_pressed="false"
    android:state_focused="true">
    <set>
      <objectAnimator android:propertyName="elevation"
        android:duration="100"
        android:valueTo="2dp"
        android:valueType="floatType"/>
    </set>
  </item>
</selector>

This animation will do the reverse of the Material Design Standard, and will take the Button elevation from 2dp to 0dp when pressed.

Now we just need to apply this animation resource to our Button style either universally or on a specific button:

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
    </style>
    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:buttonStyle">@style/NoShadowButton</item>
    </style>
    <style name="NoShadowButton" parent="android:style/Widget.Button">
        <item name="android:stateListAnimator">@anim/reverse_state_list_animator</item>
    </style>
</resources>

Now pressing any button within the AppTheme will reverse the elevation property and go more “into” the view rather than elevating.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Xamarin.Controls – Android ArcLayout

Here’s a quick and fun new control to play with. It’s called the ArcLayout, and if the name isn’t descriptive enough, it allows you to layout your Views in the layout based on arcs, degrees, radii, etc. rather than using existing LinearLayouts, RelativeLayouts, or FrameLayouts.

The control is originally from OgacleJapan and you can find his native Android repository here: https://github.com/ogaclejapan/ArcLayout.

I’ve simply provided a Xamarin Android Binding Project to wrap it which you can find here: https://github.com/SuavePirate/ArcLayout. I haven’t created a nuget package for it yet, since the binding project could use some clean up first. However, here is an easy way to get started using it right away.

  1. Clone the Xamarin Binding Library (it includes the native project .jar/.aar in it, so you can clone and start right away).
  2. Copy the project to your solution, and add it as a reference in your Xamarin Android project.
  3. Start adding it to your layouts!
<com.ogaclejapan.arclayout.ArcLayout         
         android:id="@id/arc_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="bottom"
         app:arc_color="#4D000000"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF" 
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>

Now we can dissect the new attributes and properties we get from ArcLayout:
attrs

Properties of the ArcLayout

attr description
arc_origin Center of the arc on layout. All of patterns that can be specified, see the demo app.
arc_color Arc Shaped color
arc_radius Radius of the layout
arc_axisRadius Radius the axis of the child views
arc_freeAngle If set to true, each child view can set the free angle, default false
arc_reverseAngle If set to true, reverse the order of the child, default false. Note: If arc_freeAngle set to true does not work

 

Properties of the ArcLayout Child Views

arc_origin Set the origin point of arc_axisRadius as well as layout_gravity, default center
arc_angle If arc_freeAngle set to true, layout the specified angle

Here’s a good example to get you started including a full circle layout and two semicircles:

arc_layout_full_example.axml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                     xmlns:app="http://schemas.android.com/apk/res-auto"
                 android:id="@+id/MainLayout"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
  <com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout_top"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="top"
         app:arc_color="#321321"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>
<com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="bottom"
         app:arc_color="#03a9f4"
         app:arc_radius="168dp"
         app:arc_axisRadius="120dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

</com.ogaclejapan.arclayout.ArcLayout>
<com.ogaclejapan.arclayout.ArcLayout
         android:id="@+id/arc_layout_center"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:arc_origin="center"
         app:arc_color="#4D123123"
         app:arc_radius="50dp"
         app:arc_axisRadius="50dp"
         app:arc_freeAngle="false"
         app:arc_reverseAngle="false">

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="A"
         android:textColor="#FFFFFF"
         android:background="#03A9F4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="B"
         android:textColor="#FFFFFF"
         android:background="#00BCD4"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="C"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>

    <Button
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:gravity="center"
         android:text="D"
         android:textColor="#FFFFFF"
         android:background="#009688"
         app:arc_origin="center"/>
</com.ogaclejapan.arclayout.ArcLayout>
</RelativeLayout>

arclayout

Feel free to contribute to the Xamarin Android Bindings Library repository mentioned above, I’m happy to field pull requests that can help clean it up for a real release.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tips – Android Bar Background Images in Xamarin.Forms

In a previous post, we talked about the iOS side of setting background images for both NavigationBars and TabBars. Beyond that, we looked at setting them so that we can simulate having transparent bars that show a background image behind the entire Page. That looked like this:

Simulator Screen Shot Apr 7, 2017, 11.23.33 AM

So now let’s do the same thing in Android!

Android

So Android handles images very differently compared to iOS, and also gives us a few easy ways to do what we want here. Rather than having to create custom renderers, we need to create Drawables for our images, and apply them in our Android Layouts for our Toolbar and TabBar. This approach is going to be more similar to Morgan Skinner’s approach for iOS here: http://www.morganskinner.com/2015/01/xamarin-formsusing-background-images-on.html

So we will need to crop our image where our Toolbar and Tabbar will be. If you are planning to also support landscape views, be sure to also crop the image separately for landscape and include those images in your appropriate drawables folders such as drawables-land-hdpi.

Another thing to consider is that, unlike in iOS, Android tabs are placed below the toolbar rather than at the bottom of the entire view. Because of this, you made need to use a different background for your pages that have a TabBar versus the pages that don’t. Here are some examples of how you might need to crop your images:

Portrait with no tabs

crop_portrait

Portrait with tabs

crop_portrait_tabs

Landscape with no tabs

crop_landscape

Landscape with tabs

crop_landscape_tabs

Be sure that you name the individual images the same, but place them in the appropriate resource folder as explained before. In this example we will call them:

  • toolbar_background.png
  • tabbar_background.png
  • page_background.png
  • tab_page_background.png

Unlike our iOS implementation, we do NOT need any custom renderers. Instead, we will set the background drawable of our layout files for our bars, and then in our xaml page, add an image to be the background.

Toolbar.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:minHeight="?attr/actionBarSize"
    android:background="@drawable/toolbar_background"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:layout_scrollFlags="scroll|enterAlways" /> 

Tabbar.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout         
    xmlns:android="http://schemas.android.com/apk/res/android"              
    xmlns:app="http://schemas.android.com/apk/res-auto"              
    android:id="@+id/sliding_tabs"     
    android:layout_width="match_parent"        
    android:layout_height="wrap_content"          
    android:background="@drawable/tabbar_background"          
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"          
    app:tabIndicatorColor="@android:color/white"
    app:tabGravity="fill"      
    app:tabMode="fixed" />

Then be sure to set the resources before starting Xamarin.Forms in your MainActivity:

MainActivity.cs

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());

        }
    }

With all those things set, you can now use your background images on pages with or without tabs:

ContentPageWithTabs.xaml

<ContentPage ...>
    <Grid ...>
        <Image Source="tab_page_background.png" .../>
        <!-- The rest of your content on top of the image -->
    </Grid>
</ContentPage>

ContentPageWithNoTabs.xaml

<ContentPage ...>
    <Grid ...>
        <Image Source="page_background.png" .../>
        <!-- The rest of your content on top of the image -->
    </Grid>
</ContentPage>

 

You can see an example result here (with a rushed crop job):

Screenshot_2017-04-18-14-20-08

 

 

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tips – Sending Authorized SignalR Requests

In my last post, I talked about creating a CookieProvider for handling OAuth Bearer Tokens from an HTTP Cookie. Now that we have this capability, we can make a secure connection to SignalR using a Cookie store rather than adding our tokens to the Authorization HTTP Header.

By using cookies instead of the HTTP Header, we can now use a full Websocket connection rather than being forced into long polling. Using websockets increases speed of messages received and messages sent.

So here’s the quick bit of code for making a secure SignalR Websocket connection:

SignalRConnectionService.cs

    public class SignalRConnectionService
    {
        private HubConnection _connection;
        public IHubProxy AppHubProxy { get; set; }

        public SignalRConnectionService()
        {
            _connection = new HubConnection(YOUR_SIGNALR_URL);
            AppHubProxy = _connection.CreateHubProxy("AppHub");
        }

        public async Task StartAsync()
        {
            try
            {
                if(_connection.State != ConnectionState.Disconnected)
                {
                    _connection.Stop();
                }
                _connection.CookieContainer = new CookieContainer();
                _connection.CookieContainer.Add(new Uri(THE_BASE_URL_OF_YOUR_SERVER), new Cookie("BearerToken", YOUR_ACCESS_TOKEN));
                await _connection.Start();
               
        }
        public void Stop()
        {
            _connection.Stop();
        }
    }

Now we have the full speed of websockets enabled in our Xamarin, Mac, and Windows applications!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Xamarin.Tips – Restrict the Length of Your Entry Text

Here’s a quick one on how to restrict the number of characters a user can enter in an Entry. Basically, we are going to create a custom Behavior and then apply it to our Entry.

EntryLengthValidatorBehavior.cs

 /// <summary>
    /// Behavior that restricts the length of an entry
    /// </summary>
    public class EntryLengthValidatorBehavior : Behavior<Entry>
    {
        public int MaxLength { get; set; }

        protected override void OnAttachedTo(Entry bindable)
        {
            base.OnAttachedTo(bindable);
            bindable.TextChanged += OnEntryTextChanged;
        }

        protected override void OnDetachingFrom(Entry bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.TextChanged -= OnEntryTextChanged;
        }

        void OnEntryTextChanged(object sender, TextChangedEventArgs e)
        {
            var entry = (Entry)sender;

            if (entry.Text.Length > this.MaxLength)
            {
                string entryText = entry.Text;
                entry.TextChanged -= OnEntryTextChanged;
                entry.Text = e.OldTextValue;
                entry.TextChanged += OnEntryTextChanged;
            }
        }
    }

Now we can apply it in our Xaml:

<Entry x:Name="Pin1" TextColor="White">
    <Entry.Behaviors>
        <behaviors:EntryLengthValidatorBehavior MaxLength="4"/>
    </Entry.Behaviors>
</Entry>

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Xamarin.Tips – Changing a TableView’s Separator Color

In previous posts, I talked about the misfortunes of the Xamarin.Forms TableView. In this post, we will look at changing the separator color with a custom renderer.

For some other Xamarin.Tips on upgrading your TableView, check out these two posts on customizing the Section Titles!

Android: Xamarin.Tips – Xamarin.Forms Android Custom TableView Section Titles
iOS: Xamarin.Tips – Xamarin.Forms iOS Custom TableView Section Titles

Let’s get started with a custom view that inherits the TableView and adds a BindableProperty for our SeparatorColor:

ColoredTableView.cs

    public partial class ColoredTableView : TableView
    {
        public static BindableProperty SeparatorColorProperty = BindableProperty.Create("SeparatorColor", typeof(Color), typeof(ColoredTableView), Color.White);
        public Color SeparatorColor
        {
            get
            {
                return (Color)GetValue(SeparatorColorProperty);
            }
            set
            {
                SetValue(SeparatorColorProperty, value);
            }
        }
        public ColoredTableView()
        {
            InitializeComponent();
        }
    }

Now let’s take this view and create our renderers!

Let’s start with Android:

ColoredTableViewRenderer.cs

[assembly: ExportRenderer(typeof(ColoredTableView), typeof(ColoredTableViewRenderer))]
namespace YOUR_ANDROID_NAMESPACE
{
    public class ColoredTableViewRenderer : TableViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
                return;

            var listView = Control as Android.Widget.ListView;
            var coloredTableView = (ColoredTableView)Element;
            listView.Divider = new ColorDrawable(coloredTableView.SeparatorColor.ToAndroid());
            listView.DividerHeight = 3;
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "SeparatorColor")
            {
                var listView = Control as Android.Widget.ListView;
                var coloredTableView = (ColoredTableView)Element;
                listView.Divider = new ColorDrawable(coloredTableView.SeparatorColor.ToAndroid());
            }
        }
    }
}

Basically, we take the color and apply it as a ColorDrawable, including updating when the property changes from our Forms code.

Now let’s write up our iOS renderer:

ColoredTableViewRenderer.cs

[assembly: ExportRenderer(typeof(ColoredTableView), typeof(ColoredTableViewRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class ColoredTableViewRenderer : TableViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
        {
            base.OnElementChanged(e);
            if (Control == null)
                return;

            var tableView = Control as UITableView;
            var coloredTableView = Element as ColoredTableView;
            tableView.SeparatorColor = coloredTableView.SeparatorColor.ToUIColor();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "SeparatorColor")
            {
                var tableView = Control as UITableView;
                var coloredTableView = Element as ColoredTableView;

                tableView.SeparatorColor = coloredTableView.SeparatorColor.ToUIColor();
            }
        }
    }
}

Similarly to our Android implementation, we set the SeparatorColor of our Native UITableView on set up and when the value changes.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Xamarin.Tips – Android Shadows on Transparent Views

I talked in a previous post about creating box-shadows on iOS UIViews that are transparent that you can find here: Xamarin.Tips – iOS Shadow on Transparent UIView

This is the Android counterpart! Let’s look at putting some shadows around a view with a fully transparent background. Android presented some completely different challenges compared to iOS. In the iOS solution, we ran into issues with Shadow layers requiring something in front of it to draw the drop shadow. With Android, using a CardView from the design support library gives us access to the Elevation and CardElevation properties which CAN indeed give us shadows around our view even though it is transparent. However, that kills any border on the view. Remember that we are looking for a transparent view, with a solid border, and shadows around it.

The general approach for iOS was to draw two new layers with shadows around the right and bottom sides of the UIView. Using layers allowed us to draw outside the actual frame of the view. However, with iOS, we aren’t so lucky.

So here’s the idea:
1. Adjust the margin and padding to allow us to expand the frame of the view
2. Draw a shadow gradient on the right side
3. Draw a shadow gradient on the bottom side
4. Draw a border around what our inner frame was

Here’s what that looks like in a custom renderer (since the origin of this was a custom Xamarin.Forms view):

TransparentFrameRenderer.cs

[assembly: ExportRenderer(typeof(TransparentFrame), typeof(TransparentFrameRenderer))]
namespace YOUR_ANDROID_NAMESPACE
{
    public class TransparentFrameRenderer : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                Element.Margin = new Thickness(-5);
                Element.Padding = new Thickness(15);
                Control.CardElevation = 0;
                Control.PreventCornerOverlap = false;
            }
        }

        protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);

            // draw border
            var strokePaint = new Paint();
            strokePaint.SetARGB(255, 255, 255, 255);
            strokePaint.SetStyle(Paint.Style.Stroke);
            strokePaint.StrokeWidth = 2;
            var rect = canvas.ClipBounds;
            var outline = new Rect(15, 15, rect.Right - 15, rect.Bottom - 15);

            var bottomPaint = new Paint();
            bottomPaint.SetStyle(Paint.Style.Fill);
            var shadowShader = new LinearGradient(0, 0, 0, 30, new Android.Graphics.Color(0, 0, 0, 100), new Android.Graphics.Color(0, 0, 0, 0), Shader.TileMode.Mirror);
            bottomPaint.SetShader(shadowShader);

            var rightPaint = new Paint();
            rightPaint.SetStyle(Paint.Style.Fill);
            var rightShader = new LinearGradient(0, 0, 20, 0, new Android.Graphics.Color(0, 0, 0, 100), new Android.Graphics.Color(0, 0, 0, 0), Shader.TileMode.Mirror);
            rightPaint.SetShader(rightShader);

            // draw the bottom and right shadows
            canvas.DrawRect(12, rect.Bottom - 12, rect.Right - 12, rect.Bottom + 3, bottomPaint);
            canvas.DrawRect(rect.Right - 12, 14, rect.Right + 3, rect.Bottom, rightPaint);
            canvas.DrawRect(outline, strokePaint);
        }
    }
}

Look at the Draw override. We are setting up 3 different paints. The first is the Paint for our white border. bottomPaint sets up for our bottom shadow using a LinearGradient as the shader, and similarly, rightPaint sets up our shadow for the right side.

Finally, we call DrawRect for both our shadows, then our border. The values in the DrawRect calls for the shadows are just examples to play with padding and size values. You can play with those as you see fit to make your view look best.

I’ve tested this in some large ListViews to test against the classic Android overdraw issue that can happen, but because it consists of 3 pretty simple strokes, I haven’t run into any performance issues. Hopefully this solution works for you!

This is NOT a complete solution, or a real reusable control, but more of a general approach to rendering shadows around a control with a transparent background.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!