Native Custom Layout

Fuse App SDK supports native ads and provides a built-in default layout for them. You can override the default native ad layout by registering a custom ad handler.

A custom ad handler lets you create your own UI for native ads, with full control over the layout, colors and fonts.

Keep in mind that native ads have optional elements, so your custom layout should adapt to handle optional elements (more information).

Google supports two different native ad formats - native and custom native. Make sure you register your custom ad handler for the correct native ad format.

When registering a custom ad handler, the Fuse App SDK will provide you with a zone code and an ad object.

Android — native ads

Build your layout, fill it from the ad, and return a FuseNativeAdBinding describing which view holds which asset. The SDK attaches your template and registers it, so clicks and impressions are attributed. Return null to decline the ad, which falls back to the SDK’s built-in layout.

FuseSDK.addNativeAdHandler { context, code, ad ->
    if (code != "<zone_code>") return@addNativeAdHandler null

    val template = LayoutInflater.from(context)
        .inflate(R.layout.my_native_template, null) as ViewGroup

    val headlineView = template.findViewById<TextView>(R.id.headline)
    val bodyView = template.findViewById<TextView>(R.id.body)
    val ctaView = template.findViewById<TextView>(R.id.cta)
    val iconView = template.findViewById<ImageView>(R.id.icon)

    headlineView.text = ad.headline
    bodyView.text = ad.body
    ctaView.text = ad.callToAction
    ad.icon?.let { iconView.setImageDrawable(it) }

    FuseNativeAdBinding(template).apply {
        headline = headlineView
        body = bodyView
        callToAction = ctaView
        icon = iconView
        mediaSlot = template.findViewById(R.id.media)
        adChoicesSlot = template.findViewById(R.id.ad_choices)
    }
}

For a layout you can copy and adapt, see Native Template Example.

FuseNativeAd carries headline, body, callToAction, advertiser, store, starRating, icon, hasVideo, mediaImage and mediaAspectRatio.

FuseNativeAdBinding describes your template. All fields are optional — set only what your template shows.

Field What it is
headline, body, callToAction, icon, advertiser, starRating, store Views you populated. Pass them so clicks are attributed to them.
mediaSlot, adChoicesSlot Empty containers the SDK fills. Anything already inside is removed.
mediaScaleType How the media asset is scaled.

Android — custom native ads

Custom native assets are fetched by the names you defined on the format in Google Ad Manager. Return the view you built.

FuseSDK.addCustomNativeAdHandler { context, code, ad ->
    if (code != "<zone_code>") return@addCustomNativeAdHandler null

    val template = LayoutInflater.from(context)
        .inflate(R.layout.my_custom_template, null) as ViewGroup

    template.findViewById<TextView>(R.id.headline).apply {
        text = ad.text("Headline")
        setOnClickListener { ad.click("Headline") }
    }

    val media = template.findViewById<FrameLayout>(R.id.media)
    if (ad.hasVideo) {
        ad.createVideoView(context)?.let { media.addView(it) }
    } else {
        media.addView(
            ImageView(context).apply {
                setImageDrawable(ad.image("MainImage"))
                setOnClickListener { ad.click("MainImage") }
            },
        )
    }

    template
}

Call click(assetName) only from a genuine user interaction. The SDK records the impression itself.

Android — legacy API

addGamNativeAdHandler and addGamCustomNativeAdHandler pass the ad as the linked Google Mobile Ads SDK models it, and your handler registers it with that SDK itself. These are deprecated — we recommend migrating to the newer native ads API above.

They remain available for existing integrations. Google’s Android guides describe this API: Native Ad Format, Custom Native Ad Format, Native Video Ads.

import com.publift.fuseappsdk.gma.legacy.addGamNativeAdHandler

FuseSDK.addGamNativeAdHandler { context, code, ad ->
    when (code) {
        "<zone_code>" -> MyNativeAdView(context, ad)   // ad is the Google NativeAd
        else -> null
    }
}

To migrate, swap the Google types in your handler for the Fuse ones, and return a FuseNativeAdBinding instead of registering the ad yourself:

Google Mobile Ads Fuse
nativeAd.headline, .body, .callToAction ad.headline, ad.body, ad.callToAction
nativeAd.icon?.drawable ad.icon
nativeAd.mediaContent?.aspectRatio ad.mediaAspectRatio
nativeAd.mediaContent?.hasVideoContent() ad.hasVideo
customAd.getText("X") / .getImage("X") ad.text("X") / ad.image("X")
customAd.performClick("X") ad.click("X")
NativeAdAssetNames.ASSET_ADCHOICES_CONTAINER_VIEW ad.adChoicesAssetName
view.headlineView = … then view.setNativeAd(ad) FuseNativeAdBinding(template).apply { headline = … }

In your layout, NativeAdView, MediaView and AdChoicesView become plain containers — the SDK supplies what goes into the media and ad-choices slots.

iOS

The iOS SDK passes the Google Mobile Ads object, and your handler returns the rendered view.

FuseSDK.shared.addNativeAdHandler { code, ad in
    if code == "<zone_code>" {
        return MyNativeAdView(nativeAd: ad)
    } else {
        return nil
    }
}

FuseSDK.shared.addCustomNativeAdHandler { code, ad in
    if code == "<zone_code>" {
        return MyCustomAdView(customNativeAd: ad)
    } else {
        return nil
    }
}

The Flutter and React Native SDKs do not support direct access to custom native layout. This is handled internally by the Fuse App SDK.

Additional Information

  • Custom native format asset names are defined in Google Ad Manager — these are the names you pass to ad.text(…) and ad.image(…) (Android, iOS)
  • The iOS SDK passes Google Mobile Ads types, so Google’s iOS guides apply directly: Native Ad Format, Native Video Ads