【Kotlin】XMLベースのViewをJetpack Composeへ埋め込む方法!AndroidView

【Kotlin】XMLベースのViewをJetpack Composeへ埋め込む方法!AndroidView

この記事からわかること

  • Kotlin/Android Jetpack Compose使い方
  • XMLベースのView埋め込む方法
  • AndroidView Composable関数の使い方
  • androidx.compose.ui.platform.ComposeViewの使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

従来のXMLベースでUI開発を行っていたプロジェクトをComposeに置き換えていく過程の中で「XMLベースの既存Viewを新規Composeの中で使用する方法」をまとめていきます。

XMLベースのViewをComposeの中で使用する方法

公式リファレンス: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
)

AndroidViewの使い方

実際に使用する際は引数factoryに表示させたいViewを渡します。例えばシンプルにTextViewをComposeで表示するためには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 を使用する

逆に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の破棄タイミングを明示的に決める必要があります。DisposeOnViewTreeLifecycleDestroyedLifecycle(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レイアウトを使用しない場合はそのままonCreateViewComposeViewを返してあげれば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!")
                }
            }
        }
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

Search Box

Sponsor

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index