Native is a component-based ad format that gives publishers the freedom to customize how ad assets like headlines and calls to action are presented in their apps. By choosing fonts, colors, and other details for themselves, publishers are able to create natural, unobtrusive ad presentations that can add to a rich user experience.

This codelab will walk you through using Native Advanced ads within a RecyclerView, to show you how to monetize a feed-based app. You'll start with an app that displays a list of items (current sample uses dishes from a menu), but no ads. Over the course of the codelab, you'll add code to handle two new types of items (app install and content ads), and display them correctly.

What you will build

You will build a feed-based app with ads injected at regular intervals. Your app will:

  • Request Native Ads Advanced using the Google Mobile Ads SDK
  • Present ads within your feed
  • Customize the look of the ads to match your content

What you'll learn

What you'll need

This codelab is focused on Native Advanced ads in the Google Mobile Ads SDK. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.

You will start with an app that imitates a restaurant menu. It has a RecyclerView showing a list of menu items.

Download the Code

Follow the steps listed below to download all the code for this codelab:
Click the following link to download all the code for this codelab:

  1. Download the source code from this link.
  2. Unpack the downloaded zip file.

This will unpack a root folder (admob-native-advanced-feed-master), which contains a directory specific to either Android or iOS. For this codelab you'll navigate to the Android directory. The Android directory contains the start state for this codelab, located in the work directory. The end state for this codelab is located in the final directory. Both work and final directories contain an Android project within a subdirectory named NativeRecyclerViewExample.

You'll do all of your coding in this codelab in Android project located within the work directory. If at any time you're having trouble, you can refer to the Android project located under the final directory.

Open the work project in Android Studio

  1. Open Android Studio and select Import Project.
  2. Navigate to the NativeRecyclerViewExample project under the work directory and select it.
  3. Click OK

You should now have the project open in Android Studio.

The first step to adding the Firebase SDK to your Android project is including a Firebase configuration file within your app. Normally, you would download a google-services.json file from the Firebase console and include it at the app/ directory of your app. For convenience, a sample google-services.json file has already been included in this project.

Once the google-services.json file is included in the project, import the Firebase Ads SDK by adding the following rule to your project-level build.gradle file to include the google-services plugin:

build.gradle -- NativeAdvancedRecyclerViewExample/

buildscript {
    ...
    dependencies {
        ...
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

You will also add the two lines shown below to your app-level build.gradle file. Place the compile statement inside the dependencies section and the apply plugin statement at the bottom.

build.gradle -- NativeAdvancedRecyclerViewExample/app/

  ...
  dependencies {
      ...
      compile 'com.google.firebase:firebase-ads:10.2.1'
  }
  ...

  apply plugin: 'com.google.gms.google-services'
  1. If you see a warning message across the top of the Android Studio window indicating that Gradle needs to perform a sync, click Sync Now. Gradle refreshes your project's libraries to include the dependency you just added.
  2. If you see a message asking you to install the Google Repository, just agree to the install and have Android Studio take care of the download for you. The Google Repository contains code for Gradle to incorporate.
  3. Once your build.gradle files are modified and everything has synced, try rebuilding your project (Run app in the Run menu) to make sure it compiles correctly.

You won't see any changes, but including Firebase and the Mobile Ads SDK is the first step toward getting native advanced ads into your app.

Before loading ads, your app will need to initialize the Mobile Ads SDK by calling MobileAds.initialize() with your AdMob App ID. This only needs to be done once, ideally at app launch. You can find your app's App ID in the AdMob UI. For this codelab, you will use the test app ID value of ca-app-pub-3940256099942544~3347511713. Following Android best practices, this value should be mapped to the strings resource file located at NativeAdvancedRecyclerExample/app/src/main/res/values/strings.xml of your project. Add an new entry to this file for the AdMob App ID, as shown below.

strings.xml

<resources>
   ...
   <string name="admob_app_id">ca-app-pub-3940256099942544~3347511713</string>
   ...
</resources>

Add the call to initialize(), shown below, to the onCreate() method of the MainActivity class to perform Google Mobile Ads SDK initialization.

MainActivity.java

import com.google.android.gms.ads.MobileAds;
...
public class MainActivity extends Activity {
    private RewardedVideoAd mAd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize the Google Mobile Ads SDK
        MobileAds.initialize(getApplicationContext(),
                getString(R.string.admob_app_id));
        ...
    }

    ...
}

Currently, the the RecyclerView in the sample app is displaying one type of view, views corresponding to menu items. To integrate both native app install and content ads, you'll need to define two new view types, one for each ad format.

Add the code shown below, to define two new view type constants in the RecyclerViewAdapter class.

RecyclerViewAdapter.java

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    // A menu item view type.
    private static final int MENU_ITEM_VIEW_TYPE = 0;

    // The native app install ad view type.
    private static final int NATIVE_APP_INSTALL_AD_VIEW_TYPE = 1;

    // The native content ad view type.
    private static final int NATIVE_CONTENT_AD_VIEW_TYPE = 2;
...

For each of the native advanced ad formats, there is a corresponding ad view class:

These ad view classes are ViewGroup objects that you should use as the roots for ads of each format. A single NativeContentAdView, for example, corresponds to a single content ad. Each view used to display that ad's assets (the ImageView that displays the screenshot asset, for instance) should be a child of the NativeContentAdView object.

We must also include an Ad attribution view as per Admob's policy guidelines.

Now that you have new view types for both app install and content ads, you'll need to describe how the individual views for the components of the ads should be laid out within the NativeAppInstallAdView and NativeContentAdView ad view classes. For this codelab, you'll use an layout XML resource file to define the visual structure of the ads. To make this task easier, the XML has been created for you.

To define the app install ad layout, create a file named ad_app_install.xml in the res/layout/ directory of your Android project, and include the XML content listed below in the file.

ad_app_install.xml

<com.google.android.gms.ads.formats.NativeAppInstallAdView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/menu_item_card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:orientation="vertical"
        card_view:cardCornerRadius="@dimen/cardview_default_radius"
        card_view:cardElevation="@dimen/cardview_default_elevation">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="#FFFFFF"
            android:minHeight="50dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="left"
                android:textColor="#FFFFFF"
                android:textSize="12sp"
                android:width="15dp"
                android:height="15dp"
                android:background="#FFCC66"
                android:text="Ad"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingLeft="20dp"
                android:paddingRight="20dp"
                android:paddingTop="3dp">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <ImageView
                        android:id="@+id/appinstall_app_icon"
                        android:layout_width="40dp"
                        android:layout_height="40dp"
                        android:adjustViewBounds="true"
                        android:paddingBottom="5dp" />

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical">

                        <TextView
                            android:id="@+id/appinstall_headline"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:textSize="14sp"
                            android:textStyle="bold" />

                        <RatingBar
                            android:id="@+id/appinstall_stars"
                            style="?android:attr/ratingBarStyleSmall"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:isIndicator="true"
                            android:numStars="5"
                            android:stepSize="0.5" />
                    </LinearLayout>
                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/appinstall_body"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginEnd="20dp"
                        android:layout_marginRight="20dp"
                        android:textSize="12sp" />

                    <com.google.android.gms.ads.formats.MediaView
                        android:id="@+id/appinstall_media"
                        android:layout_width="250dp"
                        android:layout_height="175dp"
                        android:layout_gravity="center_horizontal"
                        android:layout_marginTop="5dp" />

                    <LinearLayout
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="end"
                        android:orientation="horizontal"
                        android:paddingBottom="10dp"
                        android:paddingTop="10dp">

                        <TextView
                            android:id="@+id/appinstall_price"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingEnd="5dp"
                            android:paddingLeft="5dp"
                            android:paddingRight="5dp"
                            android:paddingStart="5dp"
                            android:textSize="12sp" />

                        <TextView
                            android:id="@+id/appinstall_store"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingEnd="5dp"
                            android:paddingLeft="5dp"
                            android:paddingRight="5dp"
                            android:paddingStart="5dp"
                            android:textSize="12sp" />

                        <Button
                            android:id="@+id/appinstall_call_to_action"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:gravity="center"
                            android:textSize="12sp" />
                    </LinearLayout>
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
    </android.support.v7.widget.CardView>
</com.google.android.gms.ads.formats.NativeAppInstallAdView>

For content ads, you'll create a separate XML file, named content_ad_item.xml in the Android project's res/layout/ directory, and include the XML content listed below in the file.

ad_content.xml

<com.google.android.gms.ads.formats.NativeContentAdView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#FFFFFF"
    android:minHeight="50dp">

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/menu_item_card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:orientation="vertical"
        card_view:cardCornerRadius="@dimen/cardview_default_radius"
        card_view:cardElevation="@dimen/cardview_default_elevation">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="left"
                android:textColor="#FFFFFF"
                android:textSize="12sp"
                android:width="15dp"
                android:height="15dp"
                android:background="#FFCC66"
                android:text="Ad"/>
            <TextView
                android:id="@+id/contentad_headline"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="16dp"
                android:textColor="#0000FF"
                android:textSize="24sp"
                android:textStyle="bold" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="8dp"
                android:layout_marginTop="16dp"
                android:orientation="horizontal">

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="0.35"
                    android:orientation="vertical">

                    <ImageView
                        android:id="@+id/contentad_image"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_margin="8dp"
                        android:adjustViewBounds="true"
                        android:maxHeight="100dp" />

                    <TextView
                        android:id="@+id/contentad_call_to_action"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:layout_marginTop="8dp"
                        android:textColor="#0000FF"
                        android:textSize="16sp"
                        android:textStyle="bold" />
                </LinearLayout>

                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="0.5"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/contentad_body"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:textSize="16sp" />

                    <LinearLayout
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:layout_gravity="right"
                        android:layout_marginTop="16dp"
                        android:gravity="bottom"
                        android:orientation="horizontal">

                        <TextView
                            android:id="@+id/contentad_advertiser"
                            android:layout_width="wrap_content"
                            android:layout_height="match_parent"
                            android:gravity="bottom"
                            android:textSize="14sp"
                            android:textStyle="bold" />

                        <ImageView
                            android:id="@+id/contentad_logo"
                            android:layout_width="25dp"
                            android:layout_height="25dp"
                            android:layout_marginRight="8dp"
                            android:gravity="bottom" />
                    </LinearLayout>
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
    </android.support.v7.widget.CardView>
</com.google.android.gms.ads.formats.NativeContentAdView>

Once you have defined new view types and layouts for both app install and content ads, you will need to create corresponding ViewHolder objects that will hold the native ads in a RecyclerView.

You will define two new ViewHolder classes, one to hold a NativeAppInstallAdView and another to hold a NativeContentAdView.

These ad view classes provide methods used to register the view used for each individual asset. Registering the views in this way allows the SDK to automatically handle tasks such as:

The code snippet included below is simply a demonstration of how to locate the view used to display the headline ad asset of app install ad and register it with a NativeAppInstallAdView object. Don't include this code in your project yet.

(not to be included in project)

TextView headlineView = (TextView) adView.findViewById(R.id.ad_headline);
adView.setHeadlineView(headlineView);

Create and add the NativeAppInstallAdViewHolder class, shown below, to the Android project. This class will be of type ViewHolder. It will hold an app install ad in a RecyclerView and register the view to be used for each individual asset. The process for locating each view used to display an ad asset and registering the view with the ad view object is performed as demonstrated in the the code snippet above.

NativeAppInstallAdViewHolder.java

package com.google.android.gms.example.nativeadvancedrecyclerviewexample;

import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.google.android.gms.ads.formats.MediaView;
import com.google.android.gms.ads.formats.NativeAppInstallAdView;

public class NativeAppInstallAdViewHolder extends RecyclerView.ViewHolder {
    NativeAppInstallAdViewHolder(View view) {
        super(view);
        NativeAppInstallAdView adView = (NativeAppInstallAdView) view;

        // Register the view used for each individual asset.
        // The MediaView will display a video asset if one is present in the ad, and the
        // first image asset otherwise.
        MediaView mediaView = (MediaView) adView.findViewById(R.id.appinstall_media);
        adView.setMediaView(mediaView);
        adView.setHeadlineView(adView.findViewById(R.id.appinstall_headline));
        adView.setBodyView(adView.findViewById(R.id.appinstall_body));
        adView.setCallToActionView(adView.findViewById(R.id.appinstall_call_to_action));
        adView.setIconView(adView.findViewById(R.id.appinstall_app_icon));
        adView.setPriceView(adView.findViewById(R.id.appinstall_price));
        adView.setStarRatingView(adView.findViewById(R.id.appinstall_stars));
        adView.setStoreView(adView.findViewById(R.id.appinstall_store));
    }
}

For content ads, you'll create a separate ViewHolder class, named NativeContentViewHolder. Add the class shown below to the Android project.

NativeContentAdViewHolder.java

package com.google.android.gms.example.nativeadvancedrecyclerviewexample;

import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.google.android.gms.ads.formats.NativeContentAdView;

public class NativeContentAdViewHolder extends RecyclerView.ViewHolder {
    NativeContentAdViewHolder(View view) {
        super(view);
        NativeContentAdView adView = (NativeContentAdView) view;

        // Register the view used for each individual asset.
        adView.setHeadlineView(adView.findViewById(R.id.contentad_headline));
        adView.setImageView(adView.findViewById(R.id.contentad_image));
        adView.setBodyView(adView.findViewById(R.id.contentad_body));
        adView.setCallToActionView(adView.findViewById(R.id.contentad_call_to_action));
        adView.setLogoView(adView.findViewById(R.id.contentad_logo));
        adView.setAdvertiserView(adView.findViewById(R.id.contentad_advertiser));
    }
}

As the RecyclerView in the sample app will be displaying multiple view types, the getItemViewType method of the RecyclerView.Adapter needs to be updated to return the view type of the item at each position. To determine what the ad view type should be, you'll need to check the instance of the item to be displayed at that position in the RecyclerView.

Replace the getItemViewType() method in your RecyclerViewAdapter class with the following implementation:

RecyclerViewAdapter.java

import com.google.android.gms.ads.formats.NativeAppInstallAd;
import com.google.android.gms.ads.formats.NativeContentAd;
...
@Override
public int getItemViewType(int position) {

    Object recyclerViewItem = mRecyclerViewItems.get(position);
    if (recyclerViewItem instanceof NativeAppInstallAd) {
        return NATIVE_APP_INSTALL_AD_VIEW_TYPE;
    } else if (recyclerViewItem instanceof NativeContentAd) {
        return NATIVE_CONTENT_AD_VIEW_TYPE;
    }
    return MENU_ITEM_VIEW_TYPE;
}

The onCreateViewHolder method of the RecyclerView adapter is called when RecyclerView needs a new RecyclerView.ViewHolder of the given view type to represent an item. This new ViewHolder should be constructed with a new View that can represent the items of that given type. To create NativeAppInstallAdViewHolder and NativeContentAdViewHolder objects, you'll inflate the XML layout file you created earlier.

Replace the onCreateViewHolder() method in your RecyclerViewAdapter class with the following implementation:

RecyclerViewAdapter.java

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    switch (viewType) {
        case NATIVE_APP_INSTALL_AD_VIEW_TYPE:
            View nativeAppInstallLayoutView = LayoutInflater.from(
                viewGroup.getContext()).inflate(R.layout.ad_app_install,
                viewGroup, false);
            return new NativeAppInstallAdViewHolder(nativeAppInstallLayoutView);
        case NATIVE_CONTENT_AD_VIEW_TYPE:
            View nativeContentLayoutView = LayoutInflater.from(
                viewGroup.getContext()).inflate(R.layout.ad_content,
                viewGroup, false);
            return new NativeContentAdViewHolder(nativeContentLayoutView);
        case MENU_ITEM_VIEW_TYPE:
            // Fall through.
        default:
            View menuItemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(
                R.layout.menu_item_container, viewGroup, false);
            return new MenuItemViewHolder(menuItemLayoutView);
    }
}

For each native advanced ad asset to be displayed, you must populate the asset view with the asset value from the native ad object. This process of setting the corresponding ad asset value for each ad asset view must be repeated for each native ad asset that the app will display. The populateAppInstallAdView method shown below performs these tasks for each of the assets of an app install ad.

Add this method to the RecyclerViewAdapter class.

RecyclerViewAdapter.java

import android.widget.Button;
import android.widget.RatingBar;
...
private void populateAppInstallAdView(NativeAppInstallAd nativeAppInstallAd,
    NativeAppInstallAdView adView) {

    // Some assets are guaranteed to be in every NativeAppInstallAd.
    ((ImageView) adView.getIconView()).setImageDrawable(nativeAppInstallAd.getIcon()
                .getDrawable());
    ((TextView) adView.getHeadlineView()).setText(nativeAppInstallAd.getHeadline());
    ((TextView) adView.getBodyView()).setText(nativeAppInstallAd.getBody());
    ((Button) adView.getCallToActionView()).setText(nativeAppInstallAd.getCallToAction());

    // These assets aren't guaranteed to be in every NativeAppInstallAd, so it's important to
    // check before trying to display them.
    if (nativeAppInstallAd.getPrice() == null) {
        adView.getPriceView().setVisibility(View.INVISIBLE);
    } else {
        adView.getPriceView().setVisibility(View.VISIBLE);
        ((TextView) adView.getPriceView()).setText(nativeAppInstallAd.getPrice());
    }

    if (nativeAppInstallAd.getStore() == null) {
        adView.getStoreView().setVisibility(View.INVISIBLE);
    } else {
        adView.getStoreView().setVisibility(View.VISIBLE);
        ((TextView) adView.getStoreView()).setText(nativeAppInstallAd.getStore());
    }

    if (nativeAppInstallAd.getStarRating() == null) {
        adView.getStarRatingView().setVisibility(View.INVISIBLE);
    } else {
        ((RatingBar) adView.getStarRatingView())
        .setRating(nativeAppInstallAd.getStarRating().floatValue());
        adView.getStarRatingView().setVisibility(View.VISIBLE);
    }

    // Assign native ad object to the native view.
    adView.setNativeAd(nativeAppInstallAd);
}

It is important to note, after populating the ad views, you also need to register the NativeAd object itself with the ad view object. The call to setNativeAd in the the code above performs this operation.

You will also add the populateContentAdView method, shown below, to the RecyclerViewAdapter class to populate the assets of a content ad.

RecyclerViewAdapter.java

import com.google.android.gms.ads.formats.NativeAd;
import com.google.android.gms.ads.formats.NativeAppInstallAd;
import com.google.android.gms.ads.formats.NativeAppInstallAdView;
import com.google.android.gms.ads.formats.NativeContentAd;
import com.google.android.gms.ads.formats.NativeContentAdView;
...
private void populateContentAdView(NativeContentAd nativeContentAd,
    NativeContentAdView adView) {
    // Some assets are guaranteed to be in every NativeContentAd.
    ((TextView) adView.getHeadlineView()).setText(nativeContentAd.getHeadline());
    ((TextView) adView.getBodyView()).setText(nativeContentAd.getBody());
    ((TextView) adView.getCallToActionView()).setText(nativeContentAd.getCallToAction());
    ((TextView) adView.getAdvertiserView()).setText(nativeContentAd.getAdvertiser());

    List<NativeAd.Image> images = nativeContentAd.getImages();

    if (images.size() > 0) {
        ((ImageView) adView.getImageView()).setImageDrawable(images.get(0).getDrawable());
    }

    // Some aren't guaranteed, however, and should be checked.
    NativeAd.Image logoImage = nativeContentAd.getLogo();

    if (logoImage == null) {
        adView.getLogoView().setVisibility(View.INVISIBLE);
    } else {
        ((ImageView) adView.getLogoView()).setImageDrawable(logoImage.getDrawable());
        adView.getLogoView().setVisibility(View.VISIBLE);
    }

    // Assign native ad object to the native view.
    adView.setNativeAd(nativeContentAd);
}

The last step in configuring the RecyclerView to display native advanced ads is updating the onBindViewHolder() method of RecyclerViewAdapter. The onBindViewHolder()method is called by RecyclerView to display the data at the specified position.

This method updates the contents of the RecyclerView.ViewHolder to reflect the item at the given position in a RecyclerView. Since the RecyclerView for the app supports multiple view types, you will need to call the getItemViewType method that you updated in the previous step to determine the view type of the given position.

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int viewType = getItemViewType(position);
    ...
}

Once you know the view type, you will get the item to be displayed at the specified position from mRecyclerViewItems, and cast it to the appropriate object type. For view types that are native advanced ads, you will invoke the populateAppInstallAdView() and populateContentAdView() methods you previously implemented to populate the ad assets within the ViewHolder.

Update the onBindViewHolder() method of RecyclerViewAdapter to what's shown below.

RecyclerViewAdapter.java

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int viewType = getItemViewType(position);
    switch (viewType) {
        case NATIVE_APP_INSTALL_AD_VIEW_TYPE:
            NativeAppInstallAd appInstallAd = (NativeAppInstallAd) mRecyclerViewItems.get(position);
            populateAppInstallAdView(appInstallAd, (NativeAppInstallAdView) holder.itemView);
            break;
        case NATIVE_CONTENT_AD_VIEW_TYPE:
            NativeContentAd contentAd = (NativeContentAd) mRecyclerViewItems.get(position);
            populateContentAdView(contentAd, (NativeContentAdView) holder.itemView);
            break;
        case MENU_ITEM_VIEW_TYPE:
            // fall through
        default:
            MenuItemViewHolder menuItemHolder = (MenuItemViewHolder) holder;
            MenuItem menuItem = (MenuItem) mRecyclerViewItems.get(position);

            // Get the menu item image resource ID.
            String imageName = menuItem.getImageName();
            int imageResID = mContext.getResources().getIdentifier(imageName, "drawable",
                mContext.getPackageName());

            // Add the menu item details to the menu item view.
            menuItemHolder.menuItemImage.setImageResource(imageResID);
            menuItemHolder.menuItemName.setText(menuItem.getName());
            menuItemHolder.menuItemPrice.setText(menuItem.getPrice());
            menuItemHolder.menuItemCategory.setText(menuItem.getCategory());
            menuItemHolder.menuItemDescription.setText(menuItem.getDescription());
    }
}

With code to display the native advanced ads handled, all that remains is loading the ads themselves.

To start, define a constant in the MainActivity class that will specify the number of native ads to load and an ArrayList instance variable to store the ads that have loaded.

MainActivity.java

import com.google.android.gms.ads.formats.NativeAd;
...
public class MainActivity extends AppCompatActivity {
...
    // The number of native ads to load and display.
    public static final int NUMBER_OF_ADS = 5;

    // List of native ads that have been successfully loaded.
    private List<NativeAd> mNativeAds = new ArrayList<>();
    ...

Next, define an additional constant in the string resources files (NativeAdvancedRecyclerExample/app/src/main/res/values/strings.xml) that will specify the ad unit ID to be used to request native advanced ads.

strings.xml

<resources>
   ...
   <string name="ad_unit_id">ca-app-pub-3940256099942544/8135179316</string>
   ...
</resources>

Native Advanced ads are loaded via the AdLoader class, which has its own AdLoader.Builder class to customize it during creation. There are two standard native ads advanced formats: app install and content. App install ads are represented by NativeAppInstallAd, and content ads are represented by NativeContentAd. These objects contain the ad assets for the native ad.

The code snippet included below is simply a demonstration of how to build an AdLoader that can load either an app install ad or a content ad in a single request. Don't include this code in your project yet.

(not to be included in project)

AdLoader adLoader = new AdLoader.Builder(context, ADMOB_AD_UNIT_ID)
    .forAppInstallAd(new OnAppInstallAdLoadedListener() {
        @Override
        public void onAppInstallAdLoaded(NativeAppInstallAd appInstallAd) {
            // Show the app install ad.
        }
    })
    .forContentAd(new OnContentAdLoadedListener() {
        @Override
        public void onContentAdLoaded(NativeContentAd contentAd) {
            // Show the content ad.
        }
    })
    .withAdListener(new AdListener() {
        @Override
        public void onAdFailedToLoad(int errorCode) {
            // Handle the failure by logging, altering the UI, and so on.
        }
    })
    .withNativeAdOptions(new NativeAdOptions.Builder()
        // Methods in the NativeAdOptions.Builder class can be
        // used here to specify individual options settings.
        .build())
    .build();

Since you'll need more than one ad in for the app feed, following successfully loaded or failed to load ad, you will request an additional ad until NUMBER_OF_ADS of ad requests have been made. Once you have completed the necessary number of ad requests, you will need to insert the native ad objects into the to the list of items to be displayed by the RecyclerView.

Add the insertAdsInMenuItems() method, shown below, to the MainActivity class to perform this operation.

MainActivity.java

private void insertAdsInMenuItems() {
    if (mNativeAds.size() <= 0) {
        return;
    }

    int offset = (mRecyclerViewItems.size() / mNativeAds.size()) + 1;
    int index = 0;
    for (NativeAd ad: mNativeAds) {
        mRecyclerViewItems.add(index, ad);
        index = index + offset;
    }
    loadMenu();
}

Next, add the loadNativeAd() method, shown below, to the MainActivity class to recursively load the required number of native ads. This method will invoke insertAdsInMenuItems() to add successfully loaded ads to the list of items to be displayed by the RecyclerView.

MainActivity.java

import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdLoader;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.formats.NativeAd;
import com.google.android.gms.ads.formats.NativeAppInstallAd;
import com.google.android.gms.ads.formats.NativeContentAd;
...
private void loadNativeAd(final int adLoadCount) {

    if (adLoadCount >= NUMBER_OF_ADS) {
        insertAdsInMenuItems();
        return;
    }

    AdLoader.Builder builder = new AdLoader.Builder(this, getString(R.string.ad_unit_id));
    AdLoader adLoader = builder.forAppInstallAd(new NativeAppInstallAd.OnAppInstallAdLoadedListener() {
        @Override
        public void onAppInstallAdLoaded(NativeAppInstallAd ad) {
            // An app install ad loaded successfully, call this method again to
            // load the next ad in the items list.
            mNativeAds.add(ad);
            loadNativeAd(adLoadCount + 1);

        }
    }).forContentAd(new NativeContentAd.OnContentAdLoadedListener() {
        @Override
        public void onContentAdLoaded(NativeContentAd ad) {
            // A content ad loaded successfully, call this method again to
            // load the next ad in the items list.
            mNativeAds.add(ad);
            loadNativeAd(adLoadCount + 1);
        }
    }).withAdListener(new AdListener() {
        @Override
        public void onAdFailedToLoad(int errorCode) {
            // A native ad failed to load. Call this method again to load
            // the next ad in the items list.
            Log.e("MainActivity", "The previous native ad failed to load. Attempting to" +
                " load another.");
            loadNativeAd(adLoadCount + 1);
        }
    }).build();

    // Load the Native Express ad.
    adLoader.loadAd(new AdRequest.Builder().build());
}

For convenience, create a wrapper method for loadNativeAd() that invokes it with an initial ad request count of 0. Add the code shown below to the MainActivity class.

MainActivity.java

private void loadNativeAd() {
    loadNativeAd(0);
}

This convenience wrapper for loadNativeAd() method should be invoked in the onCreate() method of MainActivity. Add the call to loadNativeAd(), after the addMenuItemsFromJson() method invocation in onCreate:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    addMenuItemsFromJson();
    loadNativeAd();
    ...
}

The final step is to present the the app feed when ads have completed loading. The loadMenu() method replaces the LoadingScreenFragment, which displays a loading spinner, with the RecyclerViewFragment, which displays the RecyclerView for the app feed. Currently, this method is invoked in onCreate() of MainActivity when the menu items for the app are ready to be shown. You will change this behavior to only display the app feed when ads are have loaded and are ready to be shown.

To start, remove the call to loadMenu()in OnCreate().

MainActivity.java

protected void onCreate(Bundle savedInstanceState) {
    ...

    loadMenu();
}

Instead, invoke loadMenu() after insertAdsInMenuItems() in loadNativeAd(), as shown below.

MainActivity.java

private void loadNativeAd(final int index) {

    if (adLoadCount >= NUMBER_OF_ADS) {
        insertAdsInMenuItems();
        loadMenu();
        return;
    }
    ...
}

Run your project

If you run your project now, you'll see app install and content ads in your feed!

Your app is now ready to display native app install and content ads using the Google Mobile Ads SDK. Run the app and scroll through the feed to see native ads inserted at regular intervals amongst menu items.

What we've covered

Next Steps

Learn More