【Jetpack Compose】Composeとxml形式のレイアウトを共存させる

## 概要
xml で作成したレイアウトの一部を Compose に置き換える方法と、反対に Compose で作成した View に xml 形式のレイアウトを組み込む方法をご紹介します。

## 環境
Android Studio verion: Android Studio Dolphin | 2021.3.1 Patch 1
kotlin version: 1.7.10
compose version: 1.3.1

## xml の一部を Compose に置き換える
Compose をレイアウトの一部にだけ導入したい場合は、xml の ```ComposeView``` タグを使います。

``` activity_replace_layout.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".ReplaceLayoutActivity">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/textXml"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:textColor="@color/black"
        android:text="Hello world from XML"
        android:layout_marginTop="12dp" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/composeView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/textXml"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="12dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

```

「Hello world from XML」と書かれたテキストの下に Compose で作成した ```Text``` と ```Button``` を追加したいと思います。

次に Activity を編集します。

``` ReplaceLayoutActivity.kt
class ReplaceLayoutActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_replace_layout)
        val composeView = findViewById<ComposeView>(R.id.composeView)

        composeView.setContent { 
            TestComposeTheme() {
                Surface (
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    Column() {
                        Text(text = "Hello world from Compose")
                        Button(onClick = {  }) {
                            Text(text = "Click!")
                        }
                    }
                }
            }
        }
    }
}
```

初めに ```ComposeView``` を id で取得します。
取得に ```findViewById``` を使っていますが、kotlin-android-extensions など他の方法でも取得可能です。

次に ```setContent``` 関数を使って Compose の View を作っていきます。
書き方は通常の ```Compose``` レイアウトにおける書き方と同じです。

以上のコードにより以下の画面が作成されます。

![Screenshot_20221201_201451_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261074/1e93287b-8ad5-8419-057d-9a45c4d54823.png)




## xml 形式のレイアウトファイルを Compose に組み込む
xml 形式のレイアウトファイルを Compose で作成したレイアウトに組み込むためには、Compose の ```AndroidView``` 関数を使用します。

この方法は　```include``` タグのほか、Jetpack Compose がまだサポートしていない xml のタグを置き換えるのに役立ちます。

今回読み込むファイルは「テストタイトル」と記載されたヘッダーです。

``` layout_header.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#0d09e7"
    android:padding="12dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="テストタイトル"
        android:textSize="20dp"
        android:textStyle="bold"
        android:textColor="@color/white"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

```

次に Activity から上記のレイアウトファイルを読み込むます。
``` ReplaceLayoutActivity.kt
Column () {
    AndroidView(
        factory = { context ->
            View.inflate(context, R.layout.layout_header, null)
        },
        modifier = Modifier.fillMaxWidth(),
        update = { v ->
            v.setOnClickListener {
                // 要素をタップしたときの処理
            }
        }
    )
    Text(text = "Hello world from Compose")
}
```

第一引数の ```factory``` で読み込みたいレイアウトファイルを inflate します。

なお、 ```factory``` に渡すラムダは ```View``` 型のデータを返す必要があります。

第二引数の ```modifier``` は必要に応じて指定してください。

第三引数の ```update``` にはこの要素に対して実施したい処理をラムダ形式で渡します。
ラムダの引数は ```View``` 型になるため、上記のように clickListener を追加することも可能です。

このように ```AndroidView``` はこれまでの ```include``` タグの代わりとして使用することができます。
表示される画面は以下の通りです。

![Screenshot_20221201_204126_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261074/62fa9df7-dc9a-b222-08f0-e4eaffde08a8.png)

## 参考
https://developer.android.com/jetpack/compose/interop/interop-apis#composition-strategy