Jetpack Compose
This guide explains how to integrate the NAM Android SDK into a UI built with Jetpack Compose.
In versions below androidx.compose:compose-bom:2024.09.03
, events such as impression or click may not trigger properly due to a Compose bug. For proper ad integration, please use version 2024.09.03
or higher.
Getting Started
Rendering NAM SDK ads in a Jetpack Compose-based UI is primarily done using AndroidView.
To prevent unnecessary or duplicate ad loading caused by recomposition, manage the AdParam
object required for ad loading as a state or pass it as a parameter to the composable rendering the ad.
When loading ads, ensure that the GfpAdLoader
is not recreated due to recomposition by managing it as a state, and use appropriate lifecycle-aware APIs such as LaunchedEffect
or DisposableEffect
to load ads through GfpAdLoader
.
Once the ad is loaded, render the ad using an AdView
or AdManager
object depending on the ad type.
When stopping ad display or leaving the screen, release resources using AndroidView.onRelease
or DisposableEffect
.
Banner Ads
Banner ads support both loading methods: using GfpAdLoader and not using it. When loading with GfpAdLoader, manage the loaded GfpBannerAdView as a state and display the ad.
Loading Banner Ads Using GfpBannerAdView
@Composable
fun BannerAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
AndroidView(
modifier = modifier,
factory = { context ->
GfpBannerAdView(context, adParam).apply {
setAdListener(/* . . . */)
loadAd()
}
},
onRelease = { view ->
view.destroy()
}
)
}
Loading Banner Ads Using GfpAdLoader
@Composable
fun BannerAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var bannerAdView by remember { mutableStateOf<GfpBannerAdView?>(null) }
LaunchedEffect(Unit) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withBannerAd {
bannerAdView = it
}
.build()
.also { loadAd() }
}
bannerAdView?.let { adView ->
AndroidView(
modifier = modifier,
factory = { adView },
onRelease = { view ->
view.destroy()
adLoader?.cancel()
}
)
}
}
Native Ads
NAM SDK's native ads currently only support Android View rendering. In a Compose environment, they are rendered using AndroidViewBinding. For integration, you will need an xml
layout file to render the ad, as well as the compose.ui:ui-viewbinding
dependency and gradle configuration.
buildFeatures {
compose = true
viewBinding = true
}
dependencies {
// . . .
implementation("androidx.compose.ui:ui-viewbinding:<compose-version>")
}
@Composable
fun NativeAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var nativeAd by remember { mutableStateOf<GfpNativeAd?>(null) }
LaunchedEffect(Unit) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withNativeAd {
nativeAd = it
}
.build()
.also { loadAd() }
}
nativeAd?.let { ad ->
AndroidViewBinding(
factory = LayoutNativeAdBinding::inflate,
modifier = modifier,
onRelease = { adLoader?.cancel() }
) {
with(nativeAdView) {
assetsContainer = assetsContainerLayout
adChoicesView = adChoices
mediaView = media
titleView = title.apply { text = ad.title }
bodyView = body.apply { text = ad.body }
callToActionView = callToAction.apply { text = ad.callToAction }
advertiserView = advertiser.apply { text = ad.advertiserName }
iconView = icon.apply {
ad.icon?.let { setImageDrawable(it.drawable) } ?: run { visibility = GONE }
}
socialContextView = socialContext.apply {
ad.socialContext?.let { text = it } ?: run { visibility = GONE }
}
noticeView = notice.apply {
ad.notice?.let { text = it } ?: run { visibility = GONE }
}
setNativeAd(ad)
}
}
}
}
The layout_native_ad.xml
code is omitted.
Native Simple Ads
Once loading is completed through GfpAdLoader
, manage the received GfpNativeSimpleAd
object as a state to ensure it is safe for recomposition. Then, set it to GfpNativeSimpleAdView
using factory
or update
in AndroidView
.
@Composable
fun NativeSimpleAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adLoader by remember { mutableStateOf<GfpAdLoader?>(null) }
var nativeSimpleAd by remember { mutableStateOf<GfpNativeSimpleAd?>(null) }
LaunchedEffect(Unit) {
adLoader = GfpAdLoader.Builder(context, adParam)
.withAdListener(/* . . . */)
.withNativeSimpleAd {
nativeSimpleAd = it
}
.build()
.also { loadAd() }
}
nativeSimpleAd?.let { ad ->
AndroidView(
modifier = modifier,
factory = { context -> GfpNativeSimpleAdView(context) },
update = { view -> view.setNativeSimpleAd(ad) },
onRelease = { adLoader?.cancel() }
)
}
}
Rewarded & Interstitial Ads
For rewarded and interstitial ads, load ads using AdManager
and manage the ad load completion status received from AdEventListener
as a state. Once the ad is loaded, display the ad button. In this case, the context
passed to the showAd()
function needs to be cast to Activity
. When releasing resources, ensure to call destroy()
using DisposableEffect.onDispose()
or similar methods.
Rewarded Ads
@Composable
fun RewardedAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adManager by remember { mutableStateOf<GfpRewardedAdManager?>(null) }
var isAdLoaded by remember { mutableStateOf(false) }
DisposableEffect(Unit) {
adManager = GfpRewardedAdManager(context, adParam).apply {
setAdListener(object : RewardedAdListener() {
override fun onAdLoaded(ad: GfpRewardedAd) {
isAdLoaded = true
}
})
loadAd()
}
onDispose {
adManager?.destroy()
}
}
if (isAdLoaded) {
Button(
onClick = { adManager?.showAd(context as Activity) },
modifier = modifier
) {
Text(text = "Show Rewarded AD")
}
}
}
Interstitial Ads
@Composable
fun InterstitialAdView(
adParam: AdParam,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
var adManager by remember { mutableStateOf<GfpInterstitialAdManager?>(null) }
var isAdLoaded by remember { mutableStateOf(false) }
DisposableEffect(Unit) {
adManager = GfpInterstitialAdManager(context, adParam).apply {
setAdListener(object : InterstitialAdListener() {
override fun onAdLoaded(ad: GfpInterstitialAd) {
isAdLoaded = true
}
})
loadAd()
}
onDispose {
adManager?.destroy()
}
}
if (isAdLoaded) {
Button(
onClick = { adManager?.showAd(context as Activity) },
modifier = modifier
) {
Text(text = "Show Interstitial AD")
}
}
}