原生广告
在 Android 自定义布局中加载和渲染 CloudX 原生广告
原生广告会公开标题、正文、图标、媒体和行动号召等独立素材,让您能够按照应用的视觉风格进行布局。CloudX 会填充已绑定的视图,并由中标网络的适配器处理展示和点击跟踪。
集成分为三个步骤:
- 绑定布局中的 UI 组件。
- 创建加载器,然后加载或渲染原生广告。
- 不再需要广告和加载器时将其销毁。
支持的网络
各网络的 Native 支持情况(包括 Native Banner 和 Native MREC 变体)以 Native 广告格式矩阵为准。各适配器的依赖和网络特定配置请参阅对应的概览页。
1. 绑定 UI 组件
创建包含所需原生广告素材的布局,然后使用 CloudXNativeAdViewBinder 映射各视图 ID。网络可能不会返回所有可选素材,因此布局应能够收起或隐藏没有内容的视图。
选项容器是必需的:CloudX 通过它显示网络提供的 AdChoices 或隐私控制。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<FrameLayout
android:id="@+id/native_ad_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/native_ad_icon"
android:layout_width="48dp"
android:layout_height="48dp" />
<TextView
android:id="@+id/native_ad_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
<TextView
android:id="@+id/native_ad_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<FrameLayout
android:id="@+id/native_ad_media_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/native_ad_advertiser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#888" />
<FrameLayout
android:id="@+id/native_ad_star_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/native_ad_cta"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</FrameLayout>val binder = CloudXNativeAdViewBinder.Builder(R.layout.native_ad_layout)
.setTitleTextViewId(R.id.native_ad_title)
.setBodyTextViewId(R.id.native_ad_body)
.setIconImageViewId(R.id.native_ad_icon)
.setMediaContentViewGroupId(R.id.native_ad_media_container)
.setCallToActionButtonId(R.id.native_ad_cta)
.setOptionsContentViewGroupId(R.id.native_ad_options)
.setAdvertiserTextViewId(R.id.native_ad_advertiser)
.setStarRatingContentViewGroupId(R.id.native_ad_star_rating)
.build()CloudXNativeAdViewBinder binder = new CloudXNativeAdViewBinder.Builder(R.layout.native_ad_layout)
.setTitleTextViewId(R.id.native_ad_title)
.setBodyTextViewId(R.id.native_ad_body)
.setIconImageViewId(R.id.native_ad_icon)
.setMediaContentViewGroupId(R.id.native_ad_media_container)
.setCallToActionButtonId(R.id.native_ad_cta)
.setOptionsContentViewGroupId(R.id.native_ad_options)
.setAdvertiserTextViewId(R.id.native_ad_advertiser)
.setStarRatingContentViewGroupId(R.id.native_ad_star_rating)
.build();星级评分
只有当网络提供的评分不低于 3.0 时,CloudX 才会填充星级评分容器;否则容器保持为空。请确保布局在星级评分容器为空时能够收起或隐藏。
2. 创建加载器
class YourActivity : AppCompatActivity(), CloudXNativeAdListener, CloudXAdRevenueListener {
private lateinit var nativeAdLoader: CloudXNativeAdLoader
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
nativeAdLoader = CloudX.createNativeAdLoader(this, "your-native-ad-unit-id")
nativeAdLoader.nativeAdListener = this
nativeAdLoader.revenueListener = this
}
override fun onDestroy() {
super.onDestroy()
nativeAdLoader.destroy()
}
}public class YourActivity extends AppCompatActivity implements CloudXNativeAdListener, CloudXAdRevenueListener {
private CloudXNativeAdLoader nativeAdLoader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
nativeAdLoader = CloudX.createNativeAdLoader(this, "your-native-ad-unit-id");
nativeAdLoader.setNativeAdListener(this);
nativeAdLoader.setRevenueListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (nativeAdLoader != null) {
nativeAdLoader.destroy();
}
}
}加载广告
请选择一种加载流程。
加载到预构建视图
目标布局已经存在时使用此流程。CloudX 会先填充并注册视图,然后通过 onNativeAdLoaded 返回该视图。
val adView = CloudXNativeAdView(this, binder)
nativeAdLoader.loadAd(adView)CloudXNativeAdView adView = new CloudXNativeAdView(this, binder);
nativeAdLoader.loadAd(adView);分别加载和渲染
如果希望在目标视图创建前预先加载广告,请使用延迟渲染。在将视图添加到视图层级之前调用 render(...)。
nativeAdLoader.loadAd()
override fun onNativeAdLoaded(adView: CloudXNativeAdView?, ad: CloudXAd) {
val newAdView = CloudXNativeAdView(this@YourActivity, binder)
nativeAdLoader.render(newAdView, ad)
container.addView(newAdView)
}nativeAdLoader.loadAd();
@Override
public void onNativeAdLoaded(CloudXNativeAdView adView, CloudXAd ad) {
CloudXNativeAdView newAdView = new CloudXNativeAdView(YourActivity.this, binder);
nativeAdLoader.render(newAdView, ad);
container.addView(newAdView);
}处理回调
已加载的原生广告会在加载后一小时过期;届时会触发 onNativeAdExpired,您可以销毁过期广告并加载新广告。在 onNativeAdLoadFailed 中,请避免立即循环重试——应延迟一段时间或在下一个自然展示时机重试。
// 必选回调
override fun onNativeAdLoaded(adView: CloudXNativeAdView?, ad: CloudXAd) {
Log.d("CloudX", "Native ad loaded from ${ad.networkName}")
ad.nativeAd?.let { nativeAd ->
if (nativeAd.isVideoContent) {
Log.d("CloudX", "Video duration: ${nativeAd.videoDuration}s")
}
}
adView?.let { container.addView(it) }
}
override fun onNativeAdLoadFailed(adUnitId: String, error: CloudXError) {
Log.e("CloudX", "Native ad failed to load: ${error.message}")
}
override fun onNativeAdClicked(ad: CloudXAd) {
Log.d("CloudX", "Native ad clicked")
}
// 可选回调(默认空实现)
override fun onNativeAdExpired(ad: CloudXAd) {
Log.d("CloudX", "Native ad expired — destroy and reload")
nativeAdLoader.destroy(ad)
nativeAdLoader.loadAd()
}
override fun onNativeAdClosed(ad: CloudXAd) {
Log.d("CloudX", "User dismissed the ad via AdChoices")
nativeAdLoader.destroy(ad)
}
// 收入回调
override fun onAdRevenuePaid(cloudXAd: CloudXAd) {
Log.d("CloudX", "Native ad revenue: ${cloudXAd.revenue} from ${cloudXAd.networkName}")
}// 必选回调
@Override
public void onNativeAdLoaded(CloudXNativeAdView adView, CloudXAd ad) {
Log.d("CloudX", "Native ad loaded from " + ad.getNetworkName());
CloudXNativeAd nativeAd = ad.getNativeAd();
if (nativeAd != null && nativeAd.isVideoContent()) {
Log.d("CloudX", "Video duration: " + nativeAd.getVideoDuration() + "s");
}
if (adView != null) {
container.addView(adView);
}
}
@Override
public void onNativeAdLoadFailed(@NonNull String adUnitId, @NonNull CloudXError error) {
Log.e("CloudX", "Native ad failed to load: " + error.getMessage());
}
@Override
public void onNativeAdClicked(@NonNull CloudXAd ad) {
Log.d("CloudX", "Native ad clicked");
}
// 可选回调(默认空实现)
@Override
public void onNativeAdExpired(@NonNull CloudXAd ad) {
Log.d("CloudX", "Native ad expired — destroy and reload");
nativeAdLoader.destroy(ad);
nativeAdLoader.loadAd();
}
@Override
public void onNativeAdClosed(@NonNull CloudXAd ad) {
Log.d("CloudX", "User dismissed the ad via AdChoices");
nativeAdLoader.destroy(ad);
}
// 收入回调
@Override
public void onAdRevenuePaid(@NonNull CloudXAd cloudXAd) {
Log.d("CloudX", "Native ad revenue: " + cloudXAd.getRevenue() + " from " + cloudXAd.getNetworkName());
}3. 销毁原生广告
广告被替换或过期时销毁单个广告;所属页面或组件结束时销毁加载器。这样可以释放网络媒体视图,防止资源随时间不断累积。
// 销毁特定已加载的广告
nativeAdLoader.destroy(ad)
// 销毁加载器及所有关联资源
nativeAdLoader.destroy()// 销毁特定已加载的广告
nativeAdLoader.destroy(ad);
// 销毁加载器及所有关联资源
nativeAdLoader.destroy();原生广告素材
CloudXNativeAd 接口可通过监听回调中的 ad.nativeAd 获取:
| 属性 | 类型 | 描述 |
|---|---|---|
title | String? | 标题文本 |
body | String? | 正文/描述文本 |
callToAction | String? | CTA 按钮文本(例如 “Install Now”) |
advertiser | String? | 广告主名称 |
icon | CloudXNativeAdImage? | 应用图标(Drawable 或 Uri) |
mainImage | CloudXNativeAdImage? | 主图片(静态素材) |
mediaView | View? | 视频/媒体播放器视图(适配器提供) |
optionsView | View? | AdChoices 或选项视图(适配器提供) |
mediaContentAspectRatio | Float | 媒体内容的宽高比 |
starRating | Double? | 应用商店评分(0–5) |
isVideoContent | Boolean | 素材是否为视频 |
videoDuration | Double | 视频时长(秒),未知时为 0.0 |
isExpired | Boolean | 广告是否已过期 |
不同网络和素材返回的资源可能不同。请将可空值视为可选值,并在素材不可用时隐藏对应 UI。mediaContentAspectRatio 或 videoDuration 为 0 表示网络尚未提供可用值。
原生视频元数据
使用 isVideoContent 区分视频和静态原生素材。当其值为 true 时,如果网络已提供时长,videoDuration 会返回以秒为单位的视频时长。渲染完成后查询时长可获得最准确的值。