🪞

Android ViewからComposableを使うと透過できない!という問題に遭遇した話

2023/11/28に公開

概要

こんにちは、@tonionagauzziです。
Androidの開発において、ViewとComposeを融合させる場面が増えています。
今回は、Android View内でComposableを使用する際に遭遇した、透過ができない問題とその解決策について説明します。

詳細

ComposeをAndroid View内で表示するために、ComposeViewを導入しました。

<androidx.compose.ui.platform.ComposeView
    android:id="@+id/compose_view"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

ところがアプリを起動すると、真っ黒な画面になってしまいました。
四隅parent指定なので、さてはComposeViewを透明にし忘れたからだな…!?と思って、XMLでandroid:backgroundを設定しました。

<androidx.compose.ui.platform.ComposeView
    android:id="@+id/compose_view"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:background="@android:color/transparent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

しかし背景が透明にならなかったので、わかった!Composableの方か!と思ってそちらで背景を透明に設定してみました。

composeView.setContent {
    Box(modifier = Modifier
        .fillMaxSize()
        .background(Color.Transparent)
    ) {
        // ...
    }
}

しかし、これでも透明になりませんでした。
この時点では、なぜだか全くわかりませんでした。

そこでLayout Inspectorを使ってView階層を確認してみると、ComposeViewとComposableの間にAndroidComposeViewという知らないViewが挟まれていることに気づきました。
さらに、そのViewが背景色を持っていることが判明しました。
Viewの構成図。AndroidComposeViewに背景色が自動適用されていた

原因

ズバリ問題の原因は、XML側の根元にあるConstraintLayoutにandroid:themeが適用されていたことでした。

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_height="0dp"
    android:layout_width="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:theme="@style/Theme.MyApp">
    ....
</androidx.constraintlayout.widget.ConstraintLayout>
<style name="Theme.MyApp" parent="Theme.MaterialComponents.Light.DarkActionBar">
    <item name="android:textColor">@color/text</item>
    <item name="android:textColorLink">@color/linkedText</item>
    <item name="android:background">@color/background</item>
</style>

このTheme.MyAppテーマが子ViewであるAndroidComposeViewにも適用されており、テーマにandroid:backgroundが設定されていたので、AndroidComposeViewに意図しない背景色が適用されていたというわけです。
修正前は全域にTheme.MyAppが適用されていた
ComposeView自体にはandroid:themeを指定していないのに…!?というのがハマりポイントです。

解決方法

この問題を解決するために、根元のConstraintLayoutにはandroid:themeを設定せず、入れ子のConstraintLayoutを持たせました。

略して書きます
<androidx.constraintlayout.widget.ConstraintLayout>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:theme="@style/Theme.MyApp">
        <!--ここにテーマを設定したいViewを書く-->
    </androidx.constraintlayout.widget.ConstraintLayout>
    <androidx.compose.ui.platform.ComposeView />
</androidx.constraintlayout.widget.ConstraintLayout>

これによって、テーマ設定が必要なViewだけにandroid:themeを適用することができました。
修正後はTheme.MyAppの適用範囲を限定した
ComposeViewにはandroid:themeの指定は不要です。
なぜならComposeView内ではMdc3ThemeなどでComposeとしてのテーマ設定が可能だからです。

おわりに

AndroidComposeViewの存在に気付かず、少し時間を費やしてしまいました。
皆さんも背景が透明にならないと感じたときは、Layout InspectorでこのViewの存在を確認してみてください。
ComposeとAndroid Viewの組み合わせにおいて、予期せぬ振る舞いに出会ったこの経験が学びとして何かの役に立つことを願っています。

Discussion