自定义原生广告格式
除了系统定义的原生广告格式之外,Ad Manager 发布商还可以通过自行指定一系列素材资源来创建自己的原生广告格式。这类格式称为自定义原生广告格式,可以用于预定型广告。这样,发布商就可以将任意结构化数据传递给应用。这些广告由 NativeCustomFormatAd
对象表示。
加载自定义原生广告格式
本指南介绍了如何加载和显示自定义原生广告格式。
构建 AdLoader
与原生广告一样,自定义原生广告格式也是使用 AdLoader
类加载的:
Java
AdLoader adLoader = new AdLoader.Builder(context, "/21775744923/example/native") .forCustomFormatAd("10063170", 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 } }) .withAdListener( ... ) .withNativeAdOptions( ... ) .build();
Kotlin
val adLoader = AdLoader.Builder(this, "/21775744923/example/native") .forCustomFormatAd("10063170", { ad -> // Show the custom format and record an impression. }, { ad, s -> // Handle the click action }) .withAdListener( ... ) .withNativeAdOptions( ... ) .build()
forCustomFormatAd
方法会将 AdLoader
配置为请求自定义的原生广告格式。系统会将如下三个参数传递到该方法中:
AdLoader
应请求的自定义原生广告格式的 ID。每种自定义的原生广告格式都有一个与之相关联的 ID。此参数指示您的应用希望AdLoader
请求哪种格式。- 广告成功加载后要调用的
OnCustomFormatAdLoadedListener
。 - 用户点按或点击广告时要调用的
OnCustomClickListener
(可选)。有关此监听器的详情,请参阅下面的“处理点击次数和展示次数”部分。
由于单个广告单元可以设置为投放多种广告素材格式,因此可以使用唯一格式 ID 多次调用 forCustomFormatAd
,以便使广告加载程序为可能存在的多种自定义原生广告格式做好准备。
自定义原生广告格式 ID
在 Ad Manager 界面的投放下拉菜单中,您可以在原生部分找到用于标识自定义原生广告格式的格式 ID:
每个自定义的原生广告格式 ID 都会显示在其名称旁边。点击其中一个名称可以转到详细信息屏幕,该屏幕中显示了相应格式中各个字段的信息:
您可以在此处添加、修改和移除个别字段。请注意每种素材资源的名称。在显示自定义原生广告格式时,名称是获取每种素材资源的数据的关键。
展示自定义的原生广告格式
自定义原生广告格式与系统定义的原生广告格式的不同之处在于:发布商有权定义自己用来制作广告的素材资源列表。因此,与系统定义的格式相比,自定义原生广告的展示过程会在以下方面有所不同:
- 由于
NativeCustomFormatAd
类用于处理您在 Ad Manager 中定义的任何自定义原生广告格式,因此它没有命名的素材资源 get 程序。它实际提供的是以字段名称作为参数的getText
和getImage
等方法。 - 没有
NativeAdView
等可用于NativeCustomFormatAd
的专用广告视图类。您可以随意使用任何有助于改善用户体验的布局。 - 由于没有专用的
ViewGroup
类,因此您不需要注册任何用于展示广告素材资源的视图。这会在展示广告时节省几行代码,但也意味着您后续需要做一点额外的工作来处理点击。
以下是一个展示 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. ... }
呈现“广告选择”图标
为支持《数字服务法案》(DSA),在欧洲经济区 (EEA) 投放的预订型广告必须包含广告选项图标和指向 Google 的“关于此广告”页面的链接。植入自定义原生广告时,您要负责呈现“广告选项”图标。我们建议您在呈现主要广告素材资源时,采取措施来呈现“广告选项”图标并设置点击监听器。
以下示例假定您已在视图层次结构中定义了 <ImageView />
元素来容纳 AdChoices 徽标。
<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>
以下示例会呈现“广告选择”图标并配置适当的点击行为。
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)
}
}
...
}
自定义原生广告格式的原生视频
创建自定义广告格式时,您可以选择调整格式,使之适用于视频。
在应用实现中,您可以使用 NativeCustomFormatAd.getMediaContent()
获取媒体内容。然后调用 setMediaContent()
以在媒体视图上设置媒体内容。如果广告没有视频内容,则需要制定备用方案,以便在没有视频的情况下也能展示广告。
以下示例代码会检查广告是否有视频内容,如果没有视频,则会在视频位置展示图片:
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) } }
如需详细了解如何定制自定义原生广告的视频体验,请参阅 MediaContent。
您可以下载 Ad Manager 自定义呈现示例,以获取实际投放的原生视频广告示例。
自定义原生广告格式的点击次数和展示次数
使用自定义原生广告格式时,您的应用负责记录展示次数,并向 Google 移动广告 SDK 报告点击事件。
记录展示次数
要记录自定义格式广告的展示次数,请对相应的 NativeCustomFormatAd
调用 recordImpression
方法:
myCustomFormatAd.recordImpression();
如果您的应用不小心针对同一个广告调用该方法两次,则 SDK 会自动阻止系统针对单个请求重复记录展示次数。
报告点击次数
如需向 SDK 报告在素材资源视图中发生了点击,请在相应 NativeCustomFormatAd
上调用 performClick
方法,并传入获得点击的素材资源的名称。例如,如果您的自定义广告格式中有一个名为“MainImage”的素材资源,并且您希望报告与该素材资源对应的 ImageView
上的点击,则代码大致会如下所示:
myCustomFormatAd.performClick("MainImage");
请注意,您不需要为广告所关联的每个视图都调用此方法。如果您有另一个名为“Caption”的字段,该字段只作展示之用,不会用于接受用户的点击或点按,则您的应用就不需要为该素材资源的视图调用 performClick
。
响应自定义点击操作
当自定义格式广告上发生点击时,SDK 可能会作出的响应有三种,具体尝试的响应顺序如下:
- 调用
AdLoader
中的OnCustomClickListener
(如果已提供)。 - 对于每个广告的深层链接网址,尝试查找内容解析器,并启动第一个能够解析网址的内容解析器。
- 打开浏览器并导航到广告的传统目标网址。
forCustomFormatAd
方法接受 OnCustomClickListener
。如果您传入监听器对象,SDK 会改为调用其 onCustomClick
方法,并且不会采取进一步操作。但是,如果您传递 null 值作为监听器,SDK 会回退到广告中注册的深层链接和/或目标网址。
自定义点击监听器可让您的应用决定响应点击的最佳操作,无论是更新界面、启动新 activity 还是仅记录点击。以下是一个只记录发生点击的示例:
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()
乍一看,存在自定义点击监听器似乎很奇怪。毕竟,您的应用刚刚告诉 SDK 发生了一次点击,为什么 SDK 应该反过来向应用报告?
这种信息流动其实有多方面的用处,但最重要的是,它让 SDK 能够保持对点击响应的控制。例如,它可以自动对已为广告素材设置的第三方跟踪网址执行 ping 操作,并在后台处理其他任务,而无需任何其他代码。