「Android Integeration with AdMob Mediation」修訂間的差異
BrandonTeng(留言 | 貢獻) |
BrandonTeng(留言 | 貢獻) |
||
行 82: | 行 82: | ||
:<p style="font-size: 18px;">1. 請參考'''[https://developers.google.com/admob/android/quick-start?hl=zh-TW AdMob for Android]''' 網站提供的方式整合Google Ads SDK,此步驟會使用到'''[[#新增應用程式|新增應用程式]]'''所取得的'''應用程式ID'''</p> | :<p style="font-size: 18px;">1. 請參考'''[https://developers.google.com/admob/android/quick-start?hl=zh-TW AdMob for Android]''' 網站提供的方式整合Google Ads SDK,此步驟會使用到'''[[#新增應用程式|新增應用程式]]'''所取得的'''應用程式ID'''</p> | ||
:<p style="font-size: 18px;">2. 下載'''[[Download MADP Android SDK | TAMedia Android SDK]]''',並參考'''[[Android Getting Started SDK8 | 開始使用Getting Started]]''' 整合TAMedia Android SDK</p> | :<p style="font-size: 18px;">2. 下載'''[[Download MADP Android SDK | TAMedia Android SDK]]''',並參考'''[[Android Getting Started SDK8 | 開始使用Getting Started]]''' 整合TAMedia Android SDK</p> | ||
:<p style="font-size: 18px;">3. 至詳細範例, 下載'''[[#詳細範例 | AdMob | :<p style="font-size: 18px;">3. 至詳細範例, 下載'''[[#詳細範例 | AdMob 21.5.0 Mediation Project]]''',並整合下列Mediation Code進開發者專案中</p> | ||
<br><br><br> | <br><br><br> | ||
於 2023年4月10日 (一) 01:40 的修訂
詳細範例
AdMob 21.5.0 Mediation Project (包含橫幅、插頁、原生廣告)。
新增應用程式
AdMob網站: https://apps.admob.com/
新增廣告單元
新增中介服務群組
進入 AdMob 網頁選擇 中介服務 > 新增中介服務群組
設定廣告格式、平台
設定中介群組名稱並點選加入廣告單元
選擇先前建立的廣告單元 (以橫幅廣告為例)
-
點選新增自訂事件,並設定下列相關資訊:
有效千次曝光出價: 越高代表該家聯播網廣告曝光的機會越高
Class Name: 根據您的專案輸入 mediation code 的 package name,如: com.taiwanmobile.pt.adp.mediation.TAMediaBanner
Parameter: 輸入您的 TAmedia 廣告版位ID
點選完成後,中介服務群組的設置便結束
TAmedia廣告版位ID
程式整合
1. 請參考AdMob for Android 網站提供的方式整合Google Ads SDK,此步驟會使用到新增應用程式所取得的應用程式ID
2. 下載 TAMedia Android SDK,並參考 開始使用Getting Started 整合TAMedia Android SDK
3. 至詳細範例, 下載 AdMob 21.5.0 Mediation Project,並整合下列Mediation Code進開發者專案中
Mediation Code
下列mediation class, 開發商於proguard中須進行保護, 避免混淆後找不到該class造成AdMob SDK拋出錯誤訊息
Could not load custom event implementation class: com.taiwanmobile.pt.adp.mediation.TAmediaNative, trying Adapter implementation class.
-keep class com.taiwanmobile.pt.adp.mediation.** {
public protected <fields>;
public protected <methods>;
}
Banner
依照下列方式實作AdMob所提供的CustomEventBanner
package com.taiwanmobile.pt.adp.mediation import android.app.Activity import android.content.Context import android.os.Bundle import android.util.Log import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdSize import com.google.android.gms.ads.mediation.MediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventBanner import com.google.android.gms.ads.mediation.customevent.CustomEventBannerListener import com.taiwanmobile.pt.adp.view.* // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAMediaBanner註冊於中介服務群組, 如 新增中介服務群組 所述 class TAMediaBanner : CustomEventBanner { private var adView: TWMAdView? = null override fun requestBannerAd( context: Context, customEventBannerListener: CustomEventBannerListener, serverParameter: String?, adSize: AdSize, mediationAdRequest: MediationAdRequest, bundle: Bundle? ) { // serverParameter: AdMob 後台設定的 TAMedia Slot ID, 會藉由此參數獲得 Log.d(TAG, "requestBannerAd($serverParameter) invoked!!") adView = TWMAdView(context as Activity, convertAdSize(adSize), serverParameter) adView?.adListener = object : TWMAdViewListener { override fun onReceiveAd(ad: TWMAd) { adView?.let{ customEventBannerListener.onAdLoaded(it) } } override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) { customEventBannerListener.onAdFailedToLoad( convertErrorCode(errorCode) ) } override fun onPresentScreen(ad: TWMAd) { customEventBannerListener.onAdOpened() } override fun onDismissScreen(ad: TWMAd) { customEventBannerListener.onAdClosed() } override fun onLeaveApplication(ad: TWMAd) { customEventBannerListener.onAdClicked() customEventBannerListener.onAdLeftApplication() } } adView?.loadAd(TWMAdRequest()) } override fun onDestroy() { adView?.destroy() } override fun onPause() {} override fun onResume() {} //將AdMob的AdSize轉換為TWMAdSize private fun convertAdSize(adSize: AdSize): TWMAdSize { return when { isAdSizeEqual(adSize, TWMAdSize.BANNER) -> { TWMAdSize.BANNER } isAdSizeEqual(adSize, TWMAdSize.BANNER_1200X627) -> { TWMAdSize.BANNER_1200X627 } isAdSizeEqual(adSize, TWMAdSize.BANNER_300X250) -> { TWMAdSize.BANNER_300X250 } isAdSizeEqual(adSize, TWMAdSize.SMART_BANNER) -> { TWMAdSize.SMART_BANNER } else -> { TWMAdSize.BANNER } } } private fun isAdSizeEqual(adSize: AdSize, twmAdSize: TWMAdSize): Boolean { return (adSize.width == twmAdSize.width && adSize.height == twmAdSize.height) } //將TAMedia ErrorCode轉換為AdMob的ErrorCode private fun convertErrorCode(errorCode: TWMAdRequest.ErrorCode): Int { return when (errorCode) { TWMAdRequest.ErrorCode.INTERNAL_ERROR -> { AdRequest.ERROR_CODE_INTERNAL_ERROR } TWMAdRequest.ErrorCode.INVALID_REQUEST -> { AdRequest.ERROR_CODE_INVALID_REQUEST } TWMAdRequest.ErrorCode.NETWORK_ERROR -> { AdRequest.ERROR_CODE_NETWORK_ERROR } TWMAdRequest.ErrorCode.NO_FILL -> { AdRequest.ERROR_CODE_NO_FILL } else -> AdRequest.ERROR_CODE_NETWORK_ERROR } } companion object { private const val TAG = "TAMediaBanner" } }
Interstitial
依照下列方式實作AdMob所提供的CustomEventInterstitial
package com.taiwanmobile.pt.adp.mediation import android.app.Activity import android.content.Context import android.os.Bundle import android.util.Log import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.mediation.MediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventInterstitial import com.google.android.gms.ads.mediation.customevent.CustomEventInterstitialListener import com.taiwanmobile.pt.adp.view.TWMAd import com.taiwanmobile.pt.adp.view.TWMAdRequest import com.taiwanmobile.pt.adp.view.TWMAdViewListener import com.taiwanmobile.pt.adp.view.TWMInterstitialAd // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAMediaInterstitial註冊於中介服務群組, 如 新增中介服務群組 所述 class TAMediaInterstitial : CustomEventInterstitial { private var interstitialAd: TWMInterstitialAd? = null override fun requestInterstitialAd( context: Context, customEventInterstitialListener: CustomEventInterstitialListener, serverParameter: String?, mediationAdRequest: MediationAdRequest, bundle: Bundle? ) { // serverParameter: AdMob 後台設定的 TAMedia Slot ID, 會藉由此參數獲得 Log.d(TAG, "requestInterstitialAd($serverParameter) ") interstitialAd = TWMInterstitialAd(context as Activity, serverParameter as String) interstitialAd?.setAdListener(object : TWMAdViewListener { override fun onReceiveAd(ad: TWMAd) { customEventInterstitialListener.onAdLoaded() } override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) { customEventInterstitialListener.onAdFailedToLoad( convertErrorCode(errorCode) ) } override fun onPresentScreen(ad: TWMAd) { customEventInterstitialListener.onAdOpened() } override fun onDismissScreen(ad: TWMAd) { customEventInterstitialListener.onAdClosed() } override fun onLeaveApplication(ad: TWMAd) { customEventInterstitialListener.onAdClicked() customEventInterstitialListener.onAdLeftApplication() } }) interstitialAd?.loadAd(TWMAdRequest()) } override fun showInterstitial() { interstitialAd?.show() } override fun onDestroy() { interstitialAd?.destroy() } override fun onPause() {} override fun onResume() {} // 將TAMedia ErrorCode轉換為AdMob的ErrorCode private fun convertErrorCode(errorCode: TWMAdRequest.ErrorCode): Int { return when (errorCode) { TWMAdRequest.ErrorCode.INTERNAL_ERROR -> { AdRequest.ERROR_CODE_INTERNAL_ERROR } TWMAdRequest.ErrorCode.INVALID_REQUEST -> { AdRequest.ERROR_CODE_INVALID_REQUEST } TWMAdRequest.ErrorCode.NETWORK_ERROR -> { AdRequest.ERROR_CODE_NETWORK_ERROR } TWMAdRequest.ErrorCode.NO_FILL -> { AdRequest.ERROR_CODE_NO_FILL } else -> AdRequest.ERROR_CODE_NETWORK_ERROR } } companion object { private const val TAG = "TAMediaInterstitial" } }
Native
依照下列方式實作AdMob所提供的CustomEventNative
從AdMob廣告設定serverParameter得知TAmedia原生廣告版位, 進而對TAmedia SDK請求原生廣告, 詳情可參考 TAmedia廣告版位ID
下列為附加功能(Optional), 預設關閉, 如需使用再將屬性設為true, 詳情可參考 TWMNativeAdOptions
videoStartUnmuted: 影片預設開啟聲音
disableImageLoading: 取消圖片下載, 加速廣告請求
videoCustomControlRequest: 取消影片內控制項
mediaPreferImage: TWMMediaView改呈現圖片
package com.taiwanmobile.pt.adp.mediation import android.content.Context import android.os.Bundle import com.google.android.gms.ads.mediation.NativeMediationAdRequest import com.google.android.gms.ads.mediation.customevent.CustomEventNative import com.google.android.gms.ads.mediation.customevent.CustomEventNativeListener import com.taiwanmobile.pt.adp.mediation.native.TANativeEventForwarder import com.taiwanmobile.pt.adp.nativead.TWMNativeAdOptions import com.taiwanmobile.pt.adp.view.TWMAdRequest import com.taiwanmobile.pt.adp.view.TWMNativeAd // 需將本class的完整類別名稱 com.taiwanmobile.pt.adp.mediation.TAmediaNative註冊於中介服務群組, 如 新增中介服務群組 所述 class TAmediaNative : CustomEventNative { private var twmNativeAd: TWMNativeAd? = null // optional. Set true when the feature below is needed. // http://wiki.tamedia.com.tw/androidDoc/library/com.taiwanmobile.pt.adp.nativead/-t-w-m-native-ad-options/index.html private val videoStartUnmuted = false private val disableImageLoading = false private val videoCustomControlRequest = false private val mediaPreferImage = false override fun onDestroy() { twmNativeAd?.destroy() } override fun onPause() {} override fun onResume() {} override fun requestNativeAd( context: Context, customEventNativeListener: CustomEventNativeListener, serverParameter: String?, nativeMediationAdRequest: NativeMediationAdRequest, extra: Bundle? ) { serverParameter?.let { twmAdUnitId -> twmNativeAd = TWMNativeAd(context, twmAdUnitId).also { nativeAd -> nativeAd.setAdListener( TANativeEventForwarder( customEventNativeListener, nativeMediationAdRequest.nativeAdRequestOptions ) ) }.apply { val request = TWMAdRequest().apply { val nativeAdOptions = mutableListOf<TWMNativeAdOptions>() if (videoStartUnmuted) { nativeAdOptions.add(TWMNativeAdOptions.VIDEO_START_UNMUTED) } if (disableImageLoading) { nativeAdOptions.add(TWMNativeAdOptions.DISABLE_IMAGE_LOADING) } if (videoCustomControlRequest) { nativeAdOptions.add(TWMNativeAdOptions.VIDEO_CUSTOM_CONTROLS_REQUESTED) } if (mediaPreferImage) { nativeAdOptions.add(TWMNativeAdOptions.MEDIA_PREFER_IMAGE) } if (nativeAdOptions.size > 0) { setNativeAdOptions(nativeAdOptions.toTypedArray()) } } loadAd(request) } } } }
依照下列方式實作CustomEventNative需用到的TANativeEventForwarder
package com.taiwanmobile.pt.adp.mediation.native
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.mediation.customevent.CustomEventNativeListener
import com.google.android.gms.ads.nativead.NativeAdOptions
import com.taiwanmobile.pt.adp.view.TWMAd
import com.taiwanmobile.pt.adp.view.TWMAdRequest
import com.taiwanmobile.pt.adp.view.TWMAdViewListener
import com.taiwanmobile.pt.adp.view.TWMNativeAd
@Suppress("DEPRECATION")
class TANativeEventForwarder(private val listener: CustomEventNativeListener,
private val options: NativeAdOptions
) : TWMAdViewListener {
override fun onDismissScreen(ad: TWMAd) {}
override fun onFailedToReceiveAd(ad: TWMAd, errorCode: TWMAdRequest.ErrorCode) {
listener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL)
}
override fun onLeaveApplication(ad: TWMAd) {
listener.onAdClicked()
listener.onAdLeftApplication()
}
override fun onPresentScreen(ad: TWMAd) {
listener.onAdImpression()
}
override fun onReceiveAd(ad: TWMAd) {
when (ad is TWMNativeAd) {
true -> {
val mapper = TANativeAdMapper(ad)
listener.onAdLoaded(mapper)
}
false -> {
listener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL)
}
}
}
}
依照下列方式實作TANativeEventForwarder需用到的TANativeAdMapper
package com.taiwanmobile.pt.adp.mediation.native
import android.view.View
import android.widget.FrameLayout
import com.google.android.gms.ads.mediation.UnifiedNativeAdMapper
import com.google.android.gms.ads.nativead.NativeAdAssetNames
import com.taiwanmobile.pt.adp.nativead.TWMMediaView
import com.taiwanmobile.pt.adp.view.TWMNativeAd
import com.taiwanmobile.pt.adp.view.TWMNativeAdView
import com.taiwanmobile.pt.guide.R
class TANativeAdMapper(private val nativeAd: TWMNativeAd) : UnifiedNativeAdMapper() {
init {
overrideClickHandling = true
overrideImpressionRecording = true
// Mapping TWMNativeAd to AdMob Native AD resource
// headline
nativeAd.nativeAdContent.longSubject?.let { headline = it }
// body
nativeAd.nativeAdContent.body?.let { body = it }
// call to action
nativeAd.nativeAdContent.callToAction?.let { callToAction = it }
// icon
nativeAd.nativeAdContent.iconSquare?.let { image ->
image.getDrawable()?.let { drawable ->
image.getUri()?.let { uri ->
icon = TANativeMappedImage(drawable, uri)
}
}
}
// mediaView
nativeAd.nativeAdContent.mediaContent?.let {
setHasVideoContent(true)
}
}
override fun trackViews(
containerView: View,
clickableAssetViews: MutableMap<String, View>,
nonClickableAssetViews: MutableMap<String, View>
) {
// TWMNativeAdView population
val twmNativeAdView = TWMNativeAdView(containerView.context).apply {
// headline
clickableAssetViews[NativeAdAssetNames.ASSET_HEADLINE]?.let { headlineView ->
setLongSubjectView(headlineView)
}
// body
clickableAssetViews[NativeAdAssetNames.ASSET_BODY]?.let { bodyView ->
setBodyView(bodyView)
}
// call to action
clickableAssetViews[NativeAdAssetNames.ASSET_CALL_TO_ACTION]?.let { ctaView ->
setCallToActionView(ctaView)
}
// icon
clickableAssetViews[NativeAdAssetNames.ASSET_ICON]?.let { iconView ->
setSquareIconView(iconView)
}
// mediaView
if (hasVideoContent()) {
clickableAssetViews[NativeAdAssetNames.ASSET_MEDIA_VIDEO]?.let { mediaView ->
nativeAd.nativeAdContent.mediaContent?.let { mediaContent ->
val taMediaView = TWMMediaView(mediaView.context).apply {
setMediaContent(mediaContent)
layoutParams = mediaView.layoutParams
}
// append TWMMediaView on AdMob MediaView
(mediaView as FrameLayout?)?.addView(taMediaView)
// specify TWMMediaView in TWMNativeAdView
setMediaView(taMediaView)
// (optional) cta text size
taMediaView.setCTATextSize(12.0f)
// (optional) video count down text size
taMediaView.setVideoCountdownTextSize(14.0f)
// (optional) volume image size
taMediaView.setVolumeImageSize(20)
}
}
} else {
clickableAssetViews[NativeAdAssetNames.ASSET_MEDIA_VIDEO]?.let { mediaView ->
mediaView.visibility = View.GONE
}
}
// final step of population, specify TWMNativeAd object to TWMNativeView
setNativeAd(nativeAd)
}
// append TWMNativeAdView on AdMob container view
twmNativeAdView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
(containerView as ViewGroup).addView(twmNativeAdView)
super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews)
}
}
依照下列方式實作TANativeAdMapper需用到的TANativeMappedImage
@file:Suppress("DEPRECATION")
package com.taiwanmobile.pt.adp.mediation.native
import android.graphics.drawable.Drawable
import android.net.Uri
import com.google.android.gms.ads.formats.NativeAd
/**
* A Image class that fits the the [NativeAd.Image] abstract class and can be filled with
* assets returned by TAmedia SDK.
*/
class TANativeMappedImage(
private val drawable: Drawable,
private val uri: Uri
) :
NativeAd.Image() {
override fun getDrawable(): Drawable = drawable
override fun getUri(): Uri = uri
override fun getScale(): Double = 1.0
}
Developer Sample Code
Banner
1. 在MainActivity.kt中輸入AdUnit Id
companion object { private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}" private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}" const val KEY_ADUNITID = "adUnitId" const val KEY_ADSIZE = "adSize" }
2. 在layout檔案中加入可放置廣告的RelativeLayout(※僅供參考,開發者可依照自訂的layout進行配置使用)
<RelativeLayout android:id="@+id/adContainer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true"> </RelativeLayout>
3. BannerActivity 程式加入Banner的宣告, 設定AdUnitId與AdSize
class BannerActivity : AppCompatActivity() { private var adView: AdView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_banner) val adContainer = findViewById<View>(R.id.adContainer) as RelativeLayout val args = intent.extras if (args != null) { val adUnitId = args.getString(MainActivity.KEY_ADUNITID) val adSize: AdSize = when (args.getString(MainActivity.KEY_ADSIZE)) { BANNER_320x50 -> { AdSize.BANNER } BANNER_300x250 -> { AdSize.MEDIUM_RECTANGLE } else -> { val displayMetrics = resources.displayMetrics val dpWidth = (displayMetrics.widthPixels / displayMetrics.density).toInt() AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize( this, dpWidth ) } }
// Banner initial adUnitId?.let { val request = AdRequest.Builder().build() adView = AdView(this@BannerActivity) adView?.adUnitId = it adView?.adSize = adSize adView?.adListener = AdListenerImpl() adView?.loadAd(request) // add view into layout adContainer.addView(adView) } } } }
Interstitial
1. 在MainActivity.kt中輸入AdUnit Id
companion object { private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}" private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}" const val KEY_ADUNITID = "adUnitId" const val KEY_ADSIZE = "adSize" }
2. InterstitialActivity 程式加入Interstitial的宣告, 設定AdUnitId
class InterstitialActivity : AppCompatActivity() {
private var interstitialAd: InterstitialAd? = null
private var adUnitId: String? = null
private lateinit var binding: ActivityInterstitialBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityInterstitialBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
val args = intent.extras
if (args != null) {
adUnitId = args.getString(MainActivity.KEY_ADUNITID)
}
}
// triggered in activity_int.xml
fun fireAd(v: View?) {
adUnitId?.let {
val adRequest = AdRequest.Builder().build()
InterstitialAd.load(this, it, adRequest, object : InterstitialAdLoadCallback() {
override fun onAdLoaded(interstitialAd: InterstitialAd) {
this@InterstitialActivity.interstitialAd = interstitialAd
Toast.makeText(
baseContext,
"onAdLoaded(interstitial) invoked!!",
Toast.LENGTH_SHORT
).show()
interstitialAd.show(this@InterstitialActivity)
}
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
Toast.makeText(
baseContext,
loadAdError.message,
Toast.LENGTH_SHORT
).show()
interstitialAd = null
}
})
}
}
}
Native
1. 在MainActivity.kt中輸入AdUnit Id
companion object { private const val ADUNIT_ID_BANNER_320x50 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_BANNER_300x250 = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_SMART_BANNER = "{AdMob Banner AdUnit Id}" private const val ADUNIT_ID_INTERSTITIAL = "{AdMob Interstitial AdUnit Id}" private const val ADUNIT_ID_NATIVE = "{AdMob Native AdUnit Id}" const val KEY_ADUNITID = "adUnitId" const val KEY_ADSIZE = "adSize" }
2. NativeActivity的layout如下, 在layout檔案中加入可放置原生廣告的nativeContainer(※這裡以FrameLayout為例,開發者可依照自訂的layout進行配置使用)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical"> <FrameLayout android:id="@+id/native_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_vertical_margin" /> </LinearLayout>
3. 準備廣告佈局native_ad.xml, 使用AdMob com.google.android.gms.ads.nativead.NativeAdView
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:background="#FFFFFF" android:minHeight="50dp" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="20dp" android:paddingTop="3dp" android:paddingRight="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/ad_app_icon" android:layout_width="40dp" android:layout_height="40dp" android:adjustViewBounds="true" android:paddingEnd="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/ad_headline" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#0000FF" android:textSize="16sp" android:textStyle="bold" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/ad_body" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:layout_marginRight="20dp" android:textSize="12sp" /> <com.google.android.gms.ads.nativead.MediaView android:id="@+id/ad_media" android:layout_width="250dp" android:layout_height="175dp" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:orientation="horizontal" android:paddingTop="10dp" android:paddingBottom="10dp"> <Button android:id="@+id/ad_call_to_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:textSize="12sp" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout> </com.google.android.gms.ads.nativead.NativeAdView>
4. 使用AdMob AdLoader載入原生廣告
當AdLoader取得原生廣告時, 產出(inflate)原生廣告佈局native_ad.xml並進行NativeAdView物件填充(population), 詳情可參考下方populateNativeAdView()
NativeAdView填充下列屬性: headline, body, call to action, icon, mediaView等資訊
填充後將native_ad.xml物件加入nativeContainer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityNativeBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
val args = intent.extras
args?.let {
val adUnitId = args.getString(MainActivity.KEY_ADUNITID) ?: ""
val adLoader = AdLoader.Builder(this, adUnitId)
.forNativeAd { ad: NativeAd ->
nativeAd = ad
val adLayout = NativeAdBinding.inflate(layoutInflater)
populateNativeAdView(ad, adLayout)
binding.nativeContainer.removeAllViews()
binding.nativeContainer.addView(adLayout.root)
// If this callback occurs after the activity is destroyed, you
// must call destroy and return or you may get a memory leak.
// Note `isDestroyed` is a method on Activity.
if (isDestroyed) {
ad.destroy()
return@forNativeAd
}
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(adError: LoadAdError) {
Toast.makeText(this@NativeActivity, "onAdFailedToLoad", Toast.LENGTH_LONG)
.show()
}
override fun onAdClicked() {
super.onAdClicked()
Toast.makeText(this@NativeActivity, "onAdClicked", Toast.LENGTH_LONG)
.show()
}
override fun onAdImpression() {
super.onAdImpression()
Toast.makeText(this@NativeActivity, "onAdImpression", Toast.LENGTH_LONG)
.show()
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
// Methods in the NativeAdOptions.Builder class can be
// used here to specify individual options settings.
.build()
)
.build()
adLoader.loadAd(AdRequest.Builder().build())
}
}
/**
* Populates a [NativeAdView] object with data from a given [NativeAd].
*
* @param nativeAd the object containing the ad's assets
* @param viewBinding the view binding object [NativeAdBinding] contains the [NativeAdView]
*/
private fun populateNativeAdView(nativeAd: NativeAd, viewBinding: NativeAdBinding) {
// fetch NativeAdView from NativeAdBinding
val nativeAdView: NativeAdView = viewBinding.root
// headline
if (nativeAd.headline == null) {
viewBinding.adHeadline.visibility = View.INVISIBLE
} else {
viewBinding.adHeadline.visibility = View.VISIBLE
viewBinding.adHeadline.text = nativeAd.headline
nativeAdView.headlineView = viewBinding.adHeadline
}
// body
if (nativeAd.body == null) {
viewBinding.adBody.visibility = View.INVISIBLE
} else {
viewBinding.adBody.visibility = View.VISIBLE
viewBinding.adBody.text = nativeAd.body
nativeAdView.bodyView = viewBinding.adBody
}
// call to action
if (nativeAd.callToAction == null) {
viewBinding.adCallToAction.visibility = View.INVISIBLE
} else {
viewBinding.adCallToAction.visibility = View.VISIBLE
viewBinding.adCallToAction.text = nativeAd.callToAction
nativeAdView.callToActionView = viewBinding.adCallToAction
}
// icon
if (nativeAd.icon == null) {
viewBinding.adAppIcon.visibility = View.GONE
} else {
nativeAd.icon?.drawable?.let {
viewBinding.adAppIcon.setImageDrawable(it)
viewBinding.adAppIcon.visibility = View.VISIBLE
nativeAdView.iconView = viewBinding.adAppIcon
}
}
// mediaView
nativeAdView.mediaView = viewBinding.adMedia
nativeAdView.setNativeAd(nativeAd)
}
5. 設計架構圖
- 藍色為TAmedia SDK原生廣告類別, 黃色為AdMob SDK原生廣告類別
- 開發者僅需在native_ad.xml使用AdMob SDK, 其他則由TAmedia mediation實作負責
- 佈局中如果有發現AdMob SDK MediaView, TAmedia SDK TWMMediaView則會主動覆蓋至上面, 負責顯示影音廣告
AdMob SDK 21.5.0 設定調整
中介程式改為TAmediaCustomEvent (Adapter)
將CustomEventBanner / CustomEventInterstitial / CustomEventNative改為TAmediaCustomEvent
Banner / Interstitial / Native等中介實作皆在TAmediaCustomEvent