Adding Ads in Xamarin Forms With Custom Renderers

June 29, 2015

Xamarin.Forms is a great way to create cross-platform UIs that will be rendered natively for you mobile devices. It is very powerful but it has its limitations. One of these limitations is that it cannot create every native control. The way that it gets around this is the ability for you to create your own custom renderers. A custom renderer lets the developer override the rendering process of a particular control.

For this blog post, we will be using Google’s AdMob service to provide the ads for us. I chose AdMob since it has support for all of the platforms and it was relatively easy to get setup. The initial code for this was taken from the open source nuget package AdMobBuddy; however, the project is an unfinished work in progress and hasn’t been updated in a while so I updated the source to suit my needs.

The first thing that we need to do is create our platform-independent control or the abstraction. This will be a class that is sub-classed from a Xamarin.Forms control. This code goes in our Xamarin.Forms PCL (Portable Class Library) UI project:


public class AdMobBuddyControl : View
{
    /// <summary>
    /// The ID of the AdMob ad to display
    /// This is the string Id from your Google Play account
    /// </summary>
    public static readonly BindableProperty AdUnitIdProperty = 
        BindableProperty.Create<AdMobBuddyControl, string>
            (p => p.AdUnitId, "");
 
    /// <summary>
    /// The ID of the AdMob ad to display
    /// This is the string Id from your Google Play account
    /// </summary>
    public string AdUnitId
    {
        get { return (string)GetValue(AdUnitIdProperty); }
        set { SetValue(AdUnitIdProperty, value); }
    }
}

There isn’t very much to the control. It is just a regular Xamarin.Forms.View with a property to store the app specific Ad Id. This is useful as it allows each platform to specify its own Id so that you can track the apps separately. Next, we need to add each of our platform-specific implementations for the control.

Let’s start with iOS:

public class AdMobBuddyRenderer : ViewRenderer
{
    GADBannerView adView;
    bool viewOnScreen = false;
 
    /// <summary>
    /// Used for registration with dependency service
    /// </summary>
    public static void Init() { }
 
    /// <summary>
    /// reload the view and hit up google admob 
    /// </summary>
    /// <param name="e"></param>
    protected override void OnElementChanged(
        ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
 
        //convert the element to the control we want
        var adMobElement = Element as AdMobBuddyControl;
 
        if (null != adMobElement)
        {
            adView = new GADBannerView(GADAdSizeCons.Banner)
            {
                AdUnitID = adMobElement.AdUnitId,
                RootViewController = UIApplication
                    .SharedApplication
                    .Windows[0].RootViewController
            };
 
            adView.AdReceived += (sender, args) =>
            {
                if (!viewOnScreen) this.AddSubview(adView);
                viewOnScreen = true;
            };
 
            adView.LoadRequest(GADRequest.Request);
            base.SetNativeControl(adView);
        }
    }
}

Android:

public class AdMobBuddyRenderer : ViewRenderer
{
    /// <summary>
    /// Used for registration with dependency service
    /// </summary>
    public static void Init() { }
 
    /// <summary>
    /// reload the view and hit up google admob 
    /// </summary>
    /// <param name="e"></param>
    protected override void OnElementChanged(
        ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
 
        //convert the element to the control we want
        var adMobElement = Element as AdMobBuddyControl;
 
        if ((adMobElement != null) && (e.OldElement == null))
        {
            AdView ad = new AdView(this.Context);
            ad.AdSize = AdSize.Banner;
            ad.AdUnitId = adMobElement.AdUnitId;
            var requestbuilder = new AdRequest.Builder();
            ad.LoadAd(requestbuilder.Build());
            this.SetNativeControl(ad);
        }
    }
}

Windows Phone:

public class AdMobBuddyRenderer : ViewRenderer
{
    /// <summary>
    /// Used for registration with dependency service
    /// </summary>
    public static void Init() { }
 
    /// <summary>
    /// reload the view and hit up google admob 
    /// </summary>
    /// <param name="e"></param>
    protected override void OnElementChanged(
        ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
 
        //convert the element to the control we want
        var adMobElement = Element as AdMobBuddyControl;
 
        if ((adMobElement != null) && (e.OldElement == null))
        {
            AdView bannerAd = new AdView
            {
                Format = AdFormats.Banner,
                AdUnitID = adMobElement.AdUnitId,
            };
            AdRequest adRequest = new AdRequest();
            bannerAd.LoadAd(adRequest);
            Children.Add(bannerAd);
        }
    }
}

Now that we have our control and the custom renderer for each platform, we need to export the renderers so that Xamarin.Form’s Dependency Service will pick it up. We need to add this line of code to the top of each of our custom renderer code files:

[assembly: ExportRenderer(typeof(AdMobBuddyControl), 
                          typeof(AdMobBuddyRenderer))]

Our control is now ready to use! Let’s implement it. In my app, I’m only displaying it on the main list page as to not annoy users. In the constructor for the page, I add it as such:

listView = new InfiniteListView
{
    HasUnevenRows = true,
    ItemTemplate = new DataTemplate(typeof(PlayerCell)),
    SeparatorColor = Color.FromHex("#ddd"),
    IsPullToRefreshEnabled = true,
    BackgroundColor = Color.Black,
    LoadMoreCommand = ViewModel.LoadMoreCommand,
    ItemsSource = ViewModel.Players,
};
 
listView.ItemTapped += OnItemSelected;
listView.Refreshing += OnListViewRefreshing;
            
var layout = new StackLayout
{
    VerticalOptions = LayoutOptions.Fill,
    Children = { listView }
};
 
if (Settings.ShowAds)
    layout.Children.Add(new AdMobBuddyControl
    {
        AdUnitId = App.AdMobId
    });
 
Content = layout;

Two things out of scope but noteworthy in the code above: I added a settings flag to be able to quickly turn on/off the ads in the app and I created a static property on the App class to be able to easily set the Ad Id when the application loads.

Custom renderers are really powerful once you get the hang of them. They can be easily swapped out by creating another renderer and then commenting/uncommenting the ExportRenderer attribute. For instance, if I wanted to use iAd instead of AdMob on iOS then I could just create this in my iOS project:

[assembly: ExportRenderer(typeof(AdMobBuddyControl), 
                          typeof(iAdBannerRenderer))]
 
public class iAdBannerRenderer : ViewRenderer
{
    protected override void OnElementChanged(
        ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
        var adBannerView = new ADBannerView(
            new System.Drawing.Rectangle(0, 386, 320, 50));
        base.SetNativeControl(adBannerView);
    }
}

To get this to work, you will need to add the appropriate GoogleAds references to your projects. The Windows Phone 8 SDK can be found on Google’s Website. Just add a reference to GoogleAds.dll from the download link. For iOS, I added AdMob from the component store. For Android, I added a reference to the GooglePlayServices nuget package by Xamarin. Make sure you add the appropriate permissions to the AndroidManifest:

com.google.android.providers.gsf.permission.READ_GSERVICES

Also add the application information:

<application android:icon="@drawable/icon">        
    <meta-data android:name="com.google.android.gms.version" 
                 android:value="@integer/google_play_services_version" />
    <activity android:name="com.google.android.gms.ads.AdActivity" />
</application>

As you can see, it isn’t too difficult to add a custom renderer to Xamarin.Forms. Hopefully this will help you to get started in customizing Xamarin.Forms to meet your needs

Information and material in our blog posts are provided "as is" with no warranties either expressed or implied. Each post is an individual expression of our Sparkies. Should you identify any such content that is harmful, malicious, sensitive or unnecessary, please contact marketing@sparkhound.com.

Meet Sparkhound

Review our capabilities and services, meet the leadership team, see our valued partnerships, and read about the hardware we've earned.

Engage with us

Get in touch with any of our offices, or check out our open career positions and consider joining Sparkhound's dynamic team.