Adopting Jetpack Compose ComposeView
In the first article we had taken a look at how to reuse Material Theming in Compose using MDC theme adapter.
In this article, let’s go over how we can start using Compose in existing view groups. The reason why we would want to do something like this is to iteratively migrate older code to Compose instead of migrating the entire project at once.
In order to use Compose in Android UI toolkit, Compose ships with these 2 classes:
Both of these classes allow us to define Composable functions in our existing Android classes. So, the obvious question is how do we decided which one to use when.
ComposeView
ComposeView
is a custom view that can host Compose UI content. We can define this in our layout XML or constructing it programatically and use ComposeView#setContent
to define the composable function for the view.
ComposeView(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
Let’s say we have a TextView
we want to migrate to Compose UI. We can replace the TextView
in our layout XML to ComposeView
with same ID.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout>
<!-- <TextView-->
<!-- android:id="@+id/textview"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:gravity="center_horizontal"-->
<!-- android:text="@string/hello_world" />-->
<androidx.compose.ui.platform.ComposeView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
We can then get the ComposeView
in our class that uses this layout XML, and setContent
binding.textView.setContent {
AppTheme {
Text(stringResource(R.string.hello_world))
}
}
That’s it, we have now replaced our TextView
from Android UI toolkit with Text
from Compose UI. This is a simple example, but in general ComposeView
is really useful for migrating simple UI components, that don’t have complex or no state to avoid bloating your class, or for reusing existing composable functions.
I personally like to avoid managing state any state in setContent
and instead move it into the composable function or create a custom view using AbstractComposeView
.
AbstractComposeView
AbstractComposeView
, as the name suggests is an abstract class and a base class for custom views implemented using Compose UI. We cannot use this in layout XML directly and instead have to extend it. All the subclasses that extend AbstractComposeView
should override Content
function, where the composable function can be defined.
AbstractComposeView(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
)
The benefit of using AbstractComposeView
over ComposeView
is, we can create custom views that host Compose UI and use them in your layout XML or construct them programatically.
class ProgressButton(
context: Context
) : AbstractComposeView(context) {
private val progressState by mutableStateOf(ButtonState.IDLE)
private val text by mutableStateOf("Click me!")
@Composable
override fun Content() {
AppTheme {
Button(
modifier = Modifier
.fillMaxWidth(),
onClick = {
// Handle clicks
}
) {
if (progressState == PROGRESS) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
} else {
Text(text = text)
}
}
}
}
}
AbstractComposeView
is a great way to move your custom views or components that have a complex internal state to Compose
to avoid bloating your current classes with the state code.
Conclusion
Obviously, this article might not have covered all the cases where you should choose one over the other. It depends on various scenarios and codebase. In general start with ComposeView
to migrate small parts of the layout to Compose
and if you think there is a lot of internal state, you can either move it to the composable function or if you want to reuse the component, you can create a custom view with AbstractComposeView
.
Once all the parts of the screen are migrated to Compose, you can completely remove the layout file and build the composable UI in your Activity or Fragment or even use the composable function as screen.
if I missed something or got anything wrong, feel free to reach out to me on Twitter or write to hello@sasikanth.dev