
안녕하세요. 제가 앱을 만들때 처럼 외부에서 만든 API의 UI의 레이아웃을 어쩔수 없이 반영해야 할 때가 있습니다. custom fragment를 사용해야 하는데 compose에서 쓰고 싶어서 뷰 기반 UI도 같이 사용하려고 사용하는 것이 상호운용성 API 입니다. FragmentContainerView 라이브러리를 사용하는 프로젝트도 별로 없는거 같은데 compose 에다 적용하는 예제는 정말 드문 것 같습니다.
fragment 안에서 compose를 사용하고 싶을 때가 있습니다. 정확히 kotlin 앱에서 xml 기반의 ui 안에 compose를 넣는 방식입니다. 이것은 ComposeView라는 API를 적용하는 방법이 있습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/hello_world"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Android!" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
기존의 UI들이 LinearLayout에 있는 구조에 ComposeView만 추가된 모습입니다.
class ExampleFragment : Fragment() {
private var _binding: FragmentExampleBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentExampleBinding.inflate(inflater, container, false)
val view = binding.root
binding.composeView.apply {
// Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
기존의 프래그먼트에서 composeview를 적용 해준 방식입니다
binding.composeView.apply {
// Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
위에 소스에서 이부분만 보면 알수있는데요. databinding 된 composeview의 apply 함수의 내용이 반영 되게 될것입니다. 내부에있는 setcontent 부분에 compose source를 넣으시면 composeview 에 반영이 됩니다.
이렇게요.

물론 컴포즈 뷰가 여러개 첨부될 가능성도 있기 때문에, 이렇게 하시는 것보다도 다음과 같이 id를 여러개 배분해주는 쪽으로 하는 것이 좋습니다.
override fun onCreateView(...): View = LinearLayout(...).apply {
addView(ComposeView(...).apply {
id = R.id.compose_view_x
...
})
addView(TextView(...))
addView(ComposeView(...).apply {
id = R.id.compose_view_y
...
})
}
}
이번에는 Compose 내부에서 fragment 를 추가하려고 합니다. 접근 방식은 AdView와 같이 Compose에서 아직 사용할 수 없는 UI 요소를 사용하려는 경우에 특히 유용합니다. 이 접근 방식을 사용하면 직접 디자인한 맞춤 뷰를 재사용할 수도 있습니다.
XML 레이아웃을 삽입하려면 androidx.compose.ui:ui-viewbinding 라이브러리에서 제공하는 AndroidViewBinding API를 사용합니다.
implementation 'androidx.compose.ui:ui-viewbinding:1.3.0'
AndroidViewBinding 컴포저블을 사용하여 Compose에 Fragment를 추가하려면 FragmentContainerView를 띄워 레이아웃을 생성합니다.
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:name="com.example.MyFragment" />
이 FragmentContainerView 레이아웃에서 원하는 fragment가 위에 올라갈 것입니다. 기존에 있는 레이아웃을 바로 띄우지는 못한다는 뜻입니다. 꼭 참고하세요
기존에 fragment의 내용을 actvity에서 출력하게 될 경우 fragmentmanager 에서 transaction을 호출하는 등 그러한 방식을 사용하였지만 compose이기 때문에 framgent를 호출할때는 Composable annotation 과 함께 AndroidViewBinding API를 적용합니다.
@Composable
fun FragmentInComposeExample() {
AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
val myFragment = fragmentContainerView.getFragment<MyFragment>()
// ...
}
}
MyFragmentLayoutBinding은 fragment가 data-binding 이 된 것으로 그것을 inflate 하는 방식입니다. 이부분은 AndroidViewBinding이 지원해주는 fragment inflating방식 이더라구요. 저에게는 가장 새로웠던 부분이었습니다.
그리고 가장 중요한건 fragmentActivity를 상속해야 합니다. 그렇지 않으면 다음 과 같은 오류가 발생합니다.
FragmentContainerView must be within a FragmentActivity to be instantiated from XML
참고로 이 방법 말고 compose코드 안에서 customview를 생성하는방식도 있습니다.
저의 경우에는 Youtube API Library UI 를 그대로 가져와야 할때 사용하였습니다. 제가 예전에 개발할때는 fragmentActivity를 사용해본 경험이 없었습니다. 그래서 조금 더 파봤는데 androidx 에서 나온 것 같네요 제가 앱 배포할때에는 jetpack이 없던 시절이라.... 하하하
제가 말씀드린 이 방법들 말고도 service, broadcastreceiver 같은 android framework를 사용하는 CompositionLocal 클래스 들도 상호운용성에 해당합니다. 그건 제가 사용하고자 하는 내용과는 좀 다른 부분인지라 자세한건 android developer가 더 상세하게 설명해주고 있으니 꼭 한번씩 봐주세요.