Custom Native Ad Formats

Select platform: Android iOS

In addition to the system-defined native formats, Ad Manager publishers have the option of creating their own native ad formats by defining custom lists of assets. These are called custom native ad formats, and can be used with reserved ads. This enables publishers to pass arbitrary structured data to their apps. These ads are represented by the NativeCustomFormatAd object.

Load custom native ad formats

This guide explains how to load and display custom native ad formats.

Load a custom native ad

Like native ads, custom native ad formats are loaded using the AdLoader class:

Java

AdLoader adLoader = new AdLoader.Builder(this, "/21775744923/example/native")
    .forCustomFormatAd("12387226",
        new NativeCustomFormatAd.OnCustomFormatAdLoadedListener() {
          @Override
          public void onCustomFormatAdLoaded(NativeCustomFormatAd ad) {
            // Show the custom format and record an impression.
          }
        },
        new NativeCustomFormatAd.OnCustomClickListener() {
          @Override
          public void onCustomClick(NativeCustomFormatAd ad, String s) {
            // Handle the click action
          }
        })
    .forCustomFormatAd("12406343",
        new NativeCustomFormatAd.OnCustomFormatAdLoadedListener() {
          @Override
          public void onCustomFormatAdLoaded(NativeCustomFormatAd ad) {
            // Show the custom format and record an impression.
          }
        },
        new NativeCustomFormatAd.OnCustomClickListener() {
          @Override
          public void onCustomClick(NativeCustomFormatAd ad, String s) {
            // Handle the click action
          }
        })
    .build();

Kotlin

val adLoader = AdLoader.Builder(this, "/21775744923/example/native")
  .forCustomFormatAd(
    "12387226",
    { customFormatAd ->
      // Show the custom format and record an impression.
    },
    { customFormatAd, s ->
      // Handle the click action
    })
  .forCustomFormatAd(
    "12406343",
    { customFormatAd ->
      // Show the custom format and record an impression.
    },
    { customFormatAd, s ->
      // Handle the click action
    })
  .build()

The forCustomFormatAd method configures the AdLoader to request custom native ad formats. You can call the method multiple times for different custom format IDs. The method accepts the following parameters:

  • The ID of the custom native ad format the AdLoader should request. Each custom native ad format has an ID associated with it. This parameter indicates which format your app wants the AdLoader to request.
  • An OnCustomFormatAdLoadedListener to be invoked when an ad has loaded successfully.
  • An optional OnCustomClickListener to be invoked when the user taps or clicks on the ad. For more on this listener, see the "Handling clicks and impressions" section.

Because a single ad unit can be set up to serve more than one creative format, forCustomFormatAd can be called multiple times with unique format IDs in order to prepare the ad loader for more than one possible custom native ad format.

Custom native ad format ID

The format ID used to identify a custom native ad format can be found in the Ad Manager UI under the Native section inside the Delivery drop-down:

Each custom native ad format ID appears beside its name. Clicking on one of the names brings you to a details screen showing information about the format's fields:

From here, individual fields can be added, edited, and removed. Note the Name of each of the assets. The name is the key used to get the data for each asset when displaying your custom native ad format.

Display custom native ad formats

Custom native ad formats differ from system-defined ones in that publishers have the power to define their own list of assets that make up an ad. Therefore, the process for displaying one differs from system-defined formats in a few ways:

  1. Text and image assets are available through getText() and getImage() getters that take the field name as a parameter.
  2. Because there is no dedicated ViewGroup class to register with Google, you need to record impressions and clicks manually.
  3. A custom native ad has a null media content if the ad does not contain a video asset.

Here's an example function that displays a NativeCustomFormatAd:

Java

public void displayCustomFormatAd (ViewGroup parent,
                                     NativeCustomFormatAd customFormatAd) {
    // Inflate a layout and add it to the parent ViewGroup.
    LayoutInflater inflater = (LayoutInflater) parent.getContext()
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View adView = inflater.inflate(R.layout.custom_format_ad, parent);

    // Locate the TextView that will hold the value for "Headline" and
    // set its text.
    TextView myHeadlineView = (TextView) adView.findViewById(R.id.headline);
    myHeadlineView.setText(customFormatAd.getText("Headline"));

    // Locate the ImageView that will hold the value for "MainImage" and
    // set its drawable.
    Button myMainImageView = (ImageView) adView.findViewById(R.id.main_image);
    myMainImageView.setImageDrawable(
            customFormatAd.getImage("MainImage").getDrawable());

    ...
    // Continue locating views and displaying assets until finished.
    ...
}

Kotlin

public fun displayCustomFormatAd (parent: ViewGroup,
                                customFormatAd: NativeCustomFormatAd) {
    val adView = layoutInflater
            .inflate(R.layout.ad_simple_custom_format, null)

    val myHeadlineView = adView.findViewById<TextView>(R.id.headline)
    myHeadlineView.setText(customFormatAd.getText("Headline"));

    // Locate the ImageView that will hold the value for "MainImage" and
    // set its drawable.
    val myMainImageView = adView.findViewById(R.id.main_image);
    myMainImageView.setImageDrawable(
            customFormatAd.getImage("MainImage").drawable);

    ...
    // Continue locating views and displaying assets until finished.
    ...
}

Native video for custom native ad formats

When creating a custom format, you have the option to make the format eligible for video.

In your app implementation, you can use NativeCustomFormatAd.getMediaContent() to get the media content. Then call setMediaContent() to set the media content on your media view. If the ad has null media content, make alternate plans to show the ad without a video.

The following example checks if the ad has video content, and displays an image in its place if a video is not available:

Java

// Called when a custom native ad loads.
@Override
public void onCustomFormatAdLoaded(final NativeCustomFormatAd ad) {

  MediaContent mediaContent = ad.getMediaContent();

  // Assumes you have a FrameLayout in your view hierarchy with the ID media_placeholder.
  FrameLayout mediaPlaceholder = (FrameLayout) findViewById(R.id.media_placeholder);

  // Apps can check the MediaContent's hasVideoContent property to determine if the
  // NativeCustomFormatAd has a video asset.
  if (mediaContent != null && mediaContent.hasVideoContent()) {
    MediaView mediaView = new MediaView(mediaPlaceholder.getContext());
    mediaView.setMediaContent(mediaContent);
    mediaPlaceholder.addView(mediaView);

    // Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
    // VideoController will call methods on this object when events occur in the video
    // lifecycle.
    VideoController vc = mediaContent.getVideoController();
    vc.setVideoLifecycleCallbacks(
        new VideoController.VideoLifecycleCallbacks() {
          @Override
          public void onVideoEnd() {
            // Publishers should allow native ads to complete video playback before
            // refreshing or replacing them with another ad in the same UI location.
            super.onVideoEnd();
          }
        });
  } else {
    ImageView mainImage = new ImageView(this);
    mainImage.setAdjustViewBounds(true);
    mainImage.setImageDrawable(ad.getImage("MainImage").getDrawable());
    mediaPlaceholder.addView(mainImage);
    mainImage.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View view) {
            ad.performClick("MainImage");
          }
        });
  }
}

Kotlin

// Called when a custom native ad loads.
NativeCustomFormatAd.OnCustomFormatAdLoadedListener { ad ->

  val mediaContent = ad.mediaContent

  // Apps can check the MediaContent's hasVideoContent property to determine if the
  // NativeCustomFormatAd has a video asset.
  if (mediaContent != null && mediaContent.hasVideoContent()) {
    val mediaView = MediaView(mediaPlaceholder.getContest())
    mediaView.mediaContent = mediaContent

    val videoController = mediaContent.videoController

    // Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
    // VideoController will call methods on this object when events occur in the video
    // lifecycle.
    if (videoController != null) {
      videoController.videoLifecycleCallbacks =
        object : VideoController.VideoLifecycleCallbacks() {
          override fun onVideoEnd() {
            // Publishers should allow native ads to complete video playback before refreshing
            // or replacing them with another ad in the same UI location.
            super.onVideoEnd()
          }
        }
    }
  } else {
    val mainImage = ImageView(this)
    mainImage.adjustViewBounds = true
    mainImage.setImageDrawable(ad.getImage("MainImage")?.drawable)

    mainImage.setOnClickListener { ad.performClick("MainImage") }
    customTemplateBinding.simplecustomMediaPlaceholder.addView(mainImage)
  }
}

Download the Ad Manager Custom Rendering example for a working example of native video in action.

See Video Ads for more information on how you can customize a custom native ad's video experience.

Render the AdChoices icon

As part of Supporting the Digital Services Act (DSA), reservation ads served in the European Economic Area (EEA) require an AdChoices icon and a link to Google's About This Ad page. When implementing custom native ads, you are responsible for rendering the AdChoices icon. We recommend you take steps to render and set the click listener for the AdChoices icon when rendering the main ad assets.

The following example assumes you have defined an <ImageView /> element in your view hierarchy to hold the AdChoices logo.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView
        android:id="@+id/adChoices"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:adjustViewBounds="true"
        android:contentDescription="AdChoices icon." />
</LinearLayout>

The following examples renders the AdChoices icon and configures the appropriate click behavior.

Java

private AdSimpleCustomTemplateBinding customTemplateBinding;

private void populateAdView(final NativeCustomFormatAd nativeCustomFormatAd) {
  // Render the AdChoices icon.
  String adChoicesKey = NativeAdAssetNames.ASSET_ADCHOICES_CONTAINER_VIEW;
  NativeAd.Image adChoicesAsset = nativeCustomFormatAd.getImage(adChoicesKey);
  if (adChoicesAsset == null) {
    customTemplateBinding.adChoices.setVisibility(View.GONE);
  } else {
    customTemplateBinding.adChoices.setVisibility(View.VISIBLE);
    customTemplateBinding.adChoices.setImageDrawable(adChoicesAsset.getDrawable());

    // Enable clicks on AdChoices.
    customTemplateBinding.adChoices.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            nativeCustomFormatAd.performClick(adChoicesKey);
          }
        });
  }
  ...
}

Kotlin

private lateinit var customTemplateBinding: AdSimpleCustomTemplateBinding

private fun populateAdView(nativeCustomFormatAd: NativeCustomFormatAd) {
  // Render the AdChoices icon.
  val adChoicesKey = NativeAdAssetNames.ASSET_ADCHOICES_CONTAINER_VIEW
  val adChoicesAsset = nativeCustomFormatAd.getImage(adChoicesKey)
  if (adChoicesAsset == null) {
    customTemplateBinding.adChoices.visibility = View.GONE
  } else {
    customTemplateBinding.adChoices.setImageDrawable(adChoicesAsset.drawable)
    customTemplateBinding.adChoices.visibility = View.VISIBLE

    // Enable clicks on AdChoices.
    customTemplateBinding.adChoices.setOnClickListener {
      nativeCustomFormatAd.performClick(adChoicesKey)
    }
  }
  ...
}

Record impressions and report clicks

Your app is responsible for recording impressions and reporting click events to Google Mobile Ads SDK.

Record impressions

To record an impression for a custom native ad, call the ad's recordImpression() method:

myCustomFormatAd.recordImpression();

If your app accidentally calls the method twice for the same ad, the SDK automatically prevents a duplicate impression from being recorded for a single request.

Report clicks

To report to the SDK that a click has occurred on an asset view, call the ad's performClick() method. Provide the name of the asset that was clicked using the same string you defined in the Ad Manager UI.

myCustomFormatAd.performClick("MainImage");

Note that you don't need to call this method for every view associated with your ad. If you had another field called "Caption" that was meant to be displayed but not clicked or tapped on by the user, your app wouldn't need to call performClick for that asset's view.

Respond to custom click actions

When a click is performed on a custom format ad, there are three possible responses from the SDK, attempted in this order:

  1. Invoke the OnCustomClickListener if one was provided.
  2. For each of the ad's deep link URLs, attempt to locate a content resolver and start the first one that resolves.
  3. Open a browser and navigate to the ad's destination URL.

To implement a custom click action, provide an OnCustomClickListener:

Java

AdLoader adLoader = new AdLoader.Builder(context, "/21775744923/example/native")
    .forCustomFormatAd("10063170",
      new NativeCustomFormatAd.OnCustomFormatAdLoadedListener() {
        // Display the ad.
      },
      new NativeCustomFormatAd.OnCustomClickListener() {
          @Override
          public void onCustomClick(NativeCustomFormatAd ad, String assetName) {
            Log.i("MyApp", "A custom click just happened for " + assetName + "!");
          }
      }).build();

Kotlin

val adLoader = AdLoader.Builder(this, "/21775744923/example/native")
    .forCustomFormatAd("10063170",
        { ad ->
            // Display the ad.
        },
        { ad, assetName ->
                Log.i("MyApp", "A custom click just happened for $assetName!")
    }).build()

At first, it might seem strange that custom click listeners exist. After all, your app just told the SDK that a click happened, so why should the SDK turn around and report that to the app?

This flow of information is useful for a few reasons, but most importantly it allows the SDK to remain in control of the response to the click. It can automatically ping third-party tracking URLs that have been set for the creative, for example, and handle other tasks behind the scenes, without any additional code.