【Kotlin】XMLベースのViewをJetpack Composeへ埋め込む方法!AndroidView
この記事からわかること
- Kotlin/Android Jetpack Composeの使い方
- XMLベースのViewを埋め込む方法
- AndroidView Composable関数の使い方
- androidx.compose.ui.platform.ComposeViewの使い方
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Narwhal Feature Drop
- Android OS:15以降
- Kotlin:2.0.21
- AGP:8.9.2
- Gradle:8.11.1
- macOS(M1):Tahoe 26.0.1
従来のXMLベースでUI開発を行っていたプロジェクトをComposeに置き換えていく過程の中で「XMLベースの既存Viewを新規Composeの中で使用する方法」をまとめていきます。
XMLベースのViewをComposeの中で使用する方法
XMLベースのView(Android View)をComposeの中で使用するにはAndroidView Composable関数(以下AndroidView)を使用します。AndroidViewは従来のAndroid View(XMLも含む)を表示するためのComposable関数になっており、Composeで新規に作っている中でも既存で実装されていたXMLのViewを呼び出して使うことができるようになっています。
またこれを使用することでCompose対応されていないViewなど(例:WebView)もComposeベースのUIで使用することができるようになります。定義を確認してみると以下の3つが定義されています。
@Composable
fun <T : View> AndroidView(
modifier: Modifier = Modifier,
factory: (Context) -> T,
update: (T) -> Unit = NoOpUpdate
)
- modifier:Modifierを指定
- factory:Viewの初回生成(Composable が Composition に入るときだけ実行)
- update:ReCompositionされるたびに呼ばれる
AndroidViewの使い方
実際に使用する際は引数factoryに表示させたいViewを渡します。例えばシンプルにAndroidViewで以下のように実装します。
@Preview
@Composable
fun SampleAndroidView() {
AndroidView(
factory = { context ->
TextView(context).apply {
text = "Hello"
}
},
// 初回生成時に"Hello"を入れず空で作成したいのであれば以下でOK
// factory = ::TextView,
)
}
既存のXMLレイアウトファイルを埋め込みたい場合でも以下のように実装することができます。updateの引数から対象のViewを参照できるのでレイアウト内のViewを探して処理することも可能です。
@Composable
fun SampleXmlAndroidView() {
AndroidView(
factory = { context ->
LayoutInflater.from(context).inflate(R.layout.custom_view, null)
},
update = { view ->
val textView = view.findViewById<TextView>(R.id.textView)
textView.text = "Updated!"
}
)
}
例にあげたWebViewの場合は以下のような実装になります。
AndroidView(
factory = ::WebView,
update = { webView ->
webView.webViewClient = WebViewClient()
webView.loadUrl(url)
}
)
View Binding/ Data Bindingが有効なXMLベースUIをComposeで使用する
XMLレイアウトベースでView Binding/Data Bindingを有効にしている場合はAndroidViewではなくAndroidViewBindingを使用します。
@Composable
fun <T : ViewBinding> AndroidViewBinding(
modifier: Modifier = Modifier,
factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
update: T.() -> Unit = {}
)
実際に使用する際は自動生成されるBindingクラスを以下のように指定してあげればOKです。。
@Composable
fun AndroidViewBindingExample() {
AndroidViewBinding(
factory = ExampleLayoutBinding::inflate,
update = {
exampleView.setBackgroundColor(Color.GRAY)
}
)
}
逆にComposeをXMLベースに埋め込む方法:ComposeView
逆にComposeをXMLベースのUIに埋め込む方法も用意されています。まずはXMLレイアウトファイル内でandroidx.compose.ui.platform.ComposeViewを使用して枠を用意します。
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Fragment側ではComposeViewを取得してsetContentを使用してComposable関数を指定します。またsetViewCompositionStrategyメソッドでComposableの破棄タイミングを明示的に決める必要があります。DisposeOnViewTreeLifecycleDestroyedはLifecycle(Fragment/Activityのライフサイクル)が破棄されたときにCompositionを破棄する設定になります。
class ExampleFragmentXml : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view = inflater.inflate(R.layout.fragment_example, container, false)
val composeView = view.findViewById<ComposeView>(R.id.compose_view)
composeView.apply {
// Lifecycle(Fragment/Activityのライフサイクル)が破棄されたときにCompositionを破棄させる
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
Text("Hello Compose!")
}
}
}
return view
}
}
またXMLレイアウトを使用しない場合はそのままonCreateViewでComposeViewを返してあげればOKです。
class ExampleFragmentNoXml : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
Text("Hello Compose!")
}
}
}
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。







