- Published on
How to use Android View inside Jetpack Compose and vice versa?
- Authors
- Name
Pre-requisites:
- Knowledge of Android App development.
- Knowledge of Android view and Jetpack compose.
Use Android View inside Composable functions.
There are 4 use-case:
- Android View and layout without view binding
- Android View with view binding.
- Android View in Lazy list
- Fragment in Compose
1. Android View and layout without view binding
You can include an Android View hierarchy in a Compose UI. This approach is particularly useful if you want to use UI elements that are not yet available in Compose, like [AdView](https://developers.google.com/android/reference/com/google/android/gms/ads/AdView)
. This approach also lets you reuse custom views you may have designed.
To include a view element or hierarchy, use the [AndroidView](https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1))
composable. AndroidView
is passed a lambda that returns a [View](https://developer.android.com/reference/android/view/View)
. AndroidView
also provides an update
callback that is called when the view is inflated. The AndroidView
recomposes whenever a State
read within the callback changes. AndroidView
, like many other built-in composables, takes a Modifier
parameter that can be used, for example, to set its position in the parent composable.
@Composable
fun CustomView(){
// ADD VIEW TO COMPOSE
AndroidView(
modifier = Modifier,
factory = {
// Creates view
MyView(context).apply {
// Sets up listeners for View -> Compose communication
setOnClickListener {
selectedItem = 1
}
}
// Other Examples
// Creates view
StyledPlayerView(context).apply {
player = exoPlayer
}
// or inflate layout inside composable
View.inflate(it, R.layout.barcode_layout, null)
},
update = { view ->
// View's been inflated or state read in this block has been updated
// Add logic here if necessary
// As selectedItem is read here, AndroidView will recompose
// whenever the state changes
// Example of Compose -> View communication
view.selectedItem = selectedItem
}
) {
}
}
Note: Prefer to construct a View in the **AndroidView**
**factory**
lambda instead of using **remember**
to hold a View reference outside of **AndroidView**
.
2. AndroidView
with view binding
To embed an XML layout, use the [AndroidViewBinding](https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidViewBinding(kotlin.Function3,%20androidx.compose.ui.Modifier,%20kotlin.Function1))
API, which is provided by the androidx.compose.ui:ui-viewbinding
library. To do this, your project must enable view binding.
@Composable
fun AndroidViewBindingExample() {
AndroidViewBinding(ExampleLayoutBinding::inflate) {
exampleView.setBackgroundColor(Color.GRAY)
}
}
3. AndroidView in Lazy lists
If you are using an AndroidView
in a Lazy list (LazyColumn
, LazyRow
, Pager
, etc.), consider using the [AndroidView](https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1,kotlin.Function1))
overload introduced in version 1.4.0-rc01. This overload allows Compose to reuse the underlying View
instance when the containing composition is reused as is the case for Lazy lists.
This overload of AndroidView
adds 2 additional parameters:
onReset
- A callback invoked to signal that theView
is about to be reused.onRelease
(optional) - A callback invoked to signal that theView
has exited the composition and will not be reused again.
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AndroidViewInLazyList() {
LazyColumn {
items(100) { index ->
AndroidView(
modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
factory = { context ->
MyView(context)
},
update = { view ->
view.selectedItem = index
},
onReset = { view ->
view.clear()
}
)
}
}
}
4. Fragments in Compose
Use the AndroidViewBinding
composable to add a Fragment
in Compose. AndroidViewBinding
has fragment-specific handling such as removing the fragment when the composable leaves the composition.
Do so by inflating an XML containing a [FragmentContainerView](https://developer.android.com/reference/androidx/fragment/app/FragmentContainerView)
as the holder for your Fragment
.
For example, if you have the my_fragment_layout.xml
defined, you could use code like this while replacing the android:name
XML attribute with your Fragment
's class name:
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.compose.snippets.interop.MyFragment" />
Inflate this fragment in Compose as follows:
@Composable
fun FragmentInComposeExample() {
AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
val myFragment = fragmentContainerView.getFragment<MyFragment>()
// ...
}
}
If you need to use multiple fragments in the same layout, ensure that you have defined a unique ID for each FragmentContainerView
.
You can see the code here.
Note: It is initial Jetpack compose project. You can see
[_use-android-view-inside-jetpack-compose_](https://github.com/KaushalVasava/Xml_And_JetpackCompose_Interoperability/tree/use-android-view-inside-jetpack-compose)
branch to learn how to use it.
Use Composable function inside Android View system.
Add required dependencies and compose compiler options.
Add below in your android
block.
buildFeatures{
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.5"
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
Add these dependencies in your dependencies
block
def compose_version = "1.4.5"
// compose dependency
implementation 'androidx.activity:activity-compose:1.7.2'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.compose.material3:material3:1.2.0-alpha06'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
1. Single Compose View in the layout
If you want to incorporate Compose UI content in a fragment or an existing View layout, use [ComposeView](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView)
and call its [setContent()](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView#setContent(kotlin.Function0))
method. ComposeView
is an Android [View](https://developer.android.com/reference/android/view/View)
.
- You can put the
ComposeView
in your XML layout just like any otherView
:
Change your existing view layout with new compose view
<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/text"
android:text="Android Text View"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Inside Activity
To use Compose in Activity, set a ComposeView
in the setContentView()
method in OnCreate()
.
// This is the code for the `MainActivity` class
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Add a `ComposeView` to the activity's layout.
val composeView = ComposeView(this)
setContent {
// This is the Composable function that will be rendered in the `ComposeView`.
YourComposable()
}
setContentView(composeView)
}
}
Inside Fragment
In your fragment add this code, use [ComposeView](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView)
and call its [setContent()](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView#setContent(kotlin.Function0))
method. ComposeView
is an Android [View](https://developer.android.com/reference/android/view/View)
.
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
// ADD THIS CODE FOR YOUR COMPOSABLE
binding.composeView.apply {
// Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Output:
Android Text View
Hello Compose!
Remove textview, because now composeview do work for you.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Output: Hello Compose!
- You can also include a
ComposeView
directly in a fragment if your full screen is built with Compose, which lets you avoid using an XML layout file entirely.
class ExampleFragmentNoXml : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
// Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
Output: Hello Compose!
ComposeView
instances in the same layout
2. Multiple If there are multiple ComposeView
elements in the same layout, each one must have a unique ID for savedInstanceState
to work.
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
return LinearLayout(requireContext()).apply {
// for vertical arrangement
orientation = LinearLayout.VERTICAL
// adds views
addView(
ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
id = R.id.compose_view_1
setContent {
Text("Hello Compose 1")
}
}
)
addView(TextView(requireContext()).apply {
text = "Hello TextView"
textSize = 20f
})
addView(
ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
id = R.id.compose_view_2
setContent {
Text("Hello Compose 2")
}
}
)
}
}
}
The ComposeView
IDs are defined in the res/values/ids.xml
file:
<resources>
<item name="compose_view_1" type="id" />
<item name="compose_view_2" type="id" />
</resources>
Layout file is just container view ie. LinearLayout. And views are added dynamically in this example.
OutPut:
Preview composables in Layout Editor
You can also preview composables within the Layout Editor for your XML layout containing a ComposeView
. Doing so lets you see how your composables look within a mixed Views and Compose layout.
Say you want to display the following composable in the Layout Editor. Note that composables annotated with @Preview
are good candidates to preview in the Layout Editor.
@Preview
@Composable
fun GreetingPreview() {
Greeting(name = "Hello Android!")
}
To display this composable, use the tools:composableName
tools attribute and set its value to the fully qualified name of the composable to preview in the layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/my_compose_view"
tools:composableName="com.example.ExmapleFragment.GreetingPreview"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
</LinearLayout>
Preview
You can see the code here.*
Note: It is initial Android View project. You can see
[_use-jetpack-compose-inside-xml-layout_](https://github.com/KaushalVasava/Xml_And_JetpackCompose_Interoperability/tree/use-jetpack-compose-inside-xml-layout)
branch to learn how to use it.
Thank you for reading.
Don’t forget to clap 👏 and follow me for more such useful articles about Android Development, Kotlin & KMP.
If you need any help related to Android, Kotlin and KMP. I’m always happy to help you. You can reach me on LinkedIn, Twitter, GitHub and Instagram.