3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LazyColumn に AdMob を表示する

Last updated at Posted at 2024-10-14

はじめに

アプリの収益化手段として、AdMob などのネットワーク広告を表示する方法があります。また、2024年現在 Android アプリの UI 構築には多くのアプリで Jetpack Compose が採用されています。しかし AdMob をはじめとする多くのネットワーク広告 SDK は View でのみ提供されています。そのため、Jetpack Compose で構築された UI に AdMob の広告を表示するには、相互運用 API を活用する必要があります。

Jetpack Compose に AdMob を表示する

Jetpack Compose に View を表示する方法は公式ドキュメントの Compose でビューを使用するに記載されています。この手順に従い AndroidView Composesable 関数を使用して AdMob を表示します。

AndroidView(
    modifier = Modifier
        .fillMaxWidth()
        .height(50.dp),
    factory = { context ->
        val adRequest = AdRequest.Builder().build()
        val adView = AdView(context)
        adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
        adView.setAdSize(AdSize.BANNER)
        adView.loadAd(adRequest)
        adView
    },
)

※ adUnitId はテスト用

LazyColumn に AdMob を表示する

LazyColumn の要素として AdMob を表示する場合、注意点がひとつあります。上記の Composable 関数を LazyColumn の要素として使用すると、広告は一応表示されますが、スクロールアウトして再度スクロールインするたびに AdView インスタンスが生成され、コンテンツの再読み込みが行われてしまいます。このため、広告が真っ白な状態で表示される時間が長くなり、クリックされにくくなる可能性があります。

output.gif

そこで公式ドキュメントの Compose でビューを使用するに戻ると、遅延リストの AndroidView という節がありました。こちらにはこのように書かれていました。和訳が機械翻訳で分かりにくかったため、原文を自分で意訳しました。

This overload allows Compose to reuse the underlying View instance when the containing composition is reused as is the case for Lazy lists.

Lazy List のようにコンポジションが再利用されるとき、View インスタンスは再利用されます。

onReset - A callback invoked to signal that the View is about to be reused. This must be non-null to enable View reuse.

onReset - View が再利用される時に呼ばれます。View の再利用を有効にするときは、non-null にする必要があります。

よって、onReset メソッドに対して non-null を設定します。

AndroidView(
    modifier = Modifier
        .fillMaxWidth()
        .height(50.dp),
    factory = { context ->
        val adRequest = AdRequest.Builder().build()
        val adView = AdView(context)
        adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
        adView.setAdSize(AdSize.BANNER)
        adView.loadAd(adRequest)
        adView
    },
    // 追加
    onReset = { },
)

その結果、AdView も再利用されるため、スクロールアウトして再度スクロールインしても、最初から広告が表示されるようになりました。

output2.gif

バージョン 1.4.0-rc01 より前の Compose では、View の再利用を自作する必要があった

さて Lazy List による View インスタンスの再利用は 遅延リストの AndroidView に記載されているように、バージョン 1.4.0-rc01 (2023年3月8日)で導入されたものです。この節では、それより前はどのように LazyColumn に AdMob を表示していたのかを紹介します。

独自に View インスタンス再利用の仕組みを構築していました。

まずは View インスタンスを Queue で管理するクラスを作成します。

abstract class ViewRecycler<V : View, P> {
    private val viewQueue = mutableListOf<V>()

    /**
     * View インスタンスを作るか、キューから取り出す
     */
    fun createOrDequeue(context: Context, param: P): V {
        return if (viewQueue.isEmpty()) {
            // キューが空の時は View を作って返却する
            createView(context, param)
        } else {
            // キューから取り出して返却する
            viewQueue.removeAt(0)
        }
    }

    /**
     * View インスタンスを作成する
     */
    abstract fun createView(context: Context, param: P): V

    /**
     * 使用が終わった View をキューに追加する
     */
    fun enqueue(view: V) {
        viewQueue.add(view)
    }
}

こちらは、様々な View に対応した abstract なクラスなので、継承して AdView クラスに対応したクラスを作成します。

class AdViewRecycler : ViewRecycler<AdView, Unit>() {
    override fun createView(context: Context, param: Unit): AdView {
        val adRequest = AdRequest.Builder().build()
        val adView = AdView(context)
        adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
        adView.setAdSize(AdSize.BANNER)
        adView.loadAd(adRequest)
        adView
    }
}

AdViewRecycler クラスのインスタンスは画面 の Composable 関数など、LazyColumn が表示されている間は生存するように設置します。

val adViewRecycler = remember {
    AdViewRecycler()
}

それを使い LazyColumn の要素にする AndroidView Composable 関数は、このように使います。

@Composable
fun RecyclableAdMobBannerAndroidView(adViewRecycler: AdViewRecycler) {
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .height(50.dp),
        factory = { context ->
            val frameLayout = FrameLayout(context)
            frameLayout.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
                override fun onViewAttachedToWindow(view: View) {
                    // FrameLayout が Attach されたら、AdView を FrameLayout に追加する
                    if (frameLayout.childCount == 0) {
                        // AdView は作成するか Queue から取り出す
                        val adView = adViewRecycler.createOrDequeue(context, Unit)
                        frameLayout.addView(adView)
                    }
                }

                override fun onViewDetachedFromWindow(view: View) {
                    // FrameLayout が Detach されたら、AdView を FrameLayout から取り除いて Queue に戻す
                    if (frameLayout.childCount >= 1) {
                        val adView = frameLayout.getChildAt(0) as AdView
                        // 親 GroupView から取り除かないと、次の追加でエラーになる
                        frameLayout.removeView(adView)
                        adViewRecycler.enqueue(adView)
                    }
                }
            })
            frameLayout
        }
    )
}

まとめ

  • LazyColumn に AdMob を表示するときは、再利用の仕組みが有効になっているかに注意する必要があります。
  • 再利用の仕組みは Compose 1.4.0-rc1 から標準搭載されています。
  • リリース日である2023年3月8日から1年半が経過しており、遅れた感のある記事ですが、Compose の進化を感じていただけると幸いです。
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?