Jetpack Compose Interoperability

For Compose, there are concerns about compatibility when using new technologies for existing projects. For Compose, at least, the integration with View should be seamless.

Flexibility in building uIs is guaranteed:

  • For the new interface, you want to use Compose, okay.
  • Compose does not support, use View.
  • Existing interface does not want to move, can not move.
  • For a part of an existing interface that you want to use Compose, you can.
  • Some UI effects you want to reuse, ok, you can just take them and embed them.

This article is a few simple calls to each other small demo, the initial use of the time can be copied and pasted very handy.

The official document: developer.android.com/jetpack/com…

Compose all the UI in your Activity or Fragment

Use Compose in Activity

class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")}}}}@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")}Copy the code

Use Compose in Fragment

class PureComposeFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup? , savedInstanceState:Bundle?).: View {
        return ComposeView(requireContext()).apply {
            setContent {
                MaterialTheme {
                    Text("Hello Compose!")}}}}}Copy the code

Use Compose in the View

ComposeView is embedded in Xml:

Add ComposeView to a plain XML layout file:


      
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/hello_world"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello from XML layout" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
Copy the code

To use this method, first look up by id, and then setContent:

class ComposeViewInXmlActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_compose_view_in_xml)

        findViewById<ComposeView>(R.id.compose_view).setContent {
            // In Compose world
            MaterialTheme {
                Text("Hello Compose!")}}}}Copy the code

Dynamically add ComposeView

Adding a View in code using addView() also works for ComposeView:

class ComposeViewInViewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)

        setContentView(LinearLayout(this).apply {
            orientation = VERTICAL
            addView(ComposeView(this@ComposeViewInViewActivity).apply {
                id = R.id.compose_view_x
                setContent {
                    MaterialTheme {
                        Text("Hello Compose View 1")
                    }
                }
            })
            addView(TextView(context).apply {
                text = "I'm am old TextView"
            })
            addView(ComposeView(context).apply {
                id = R.id.compose_view_y
                setContent {
                    MaterialTheme {
                        Text("Hello Compose View 2")}}})})}}Copy the code

Here we add three children to the LinearLayout: two ComposeViews and a TextView in between.

The ComposeView, which acts as a bridge, is a ViewGroup that is itself a View, so it can be mixed into the View hierarchy tree, and its setContent() method opens up the Compose world. Here you can pass in the composable method to draw the UI.

Use View in Compose

Compose is now used to build the UI. When do you need to embed a View in your UI?

  • There is no Compose version of the View to use, for exampleAdView.MapView.WebView.
  • There’s a piece of UI I wrote earlier that I don’t want to touch (for now or ever) and want to use directly.
  • Because Compose cannot achieve the desired effect, use the View instead.

Add Android View to Compose

Example:

@Composable
fun CustomView(a) {
    val state = remember { mutableStateOf(0)}//widget.Button
    AndroidView(
        factory = { ctx ->
            //Here you can construct your View
            android.widget.Button(ctx).apply {
                text = "My Button"
                layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
                setOnClickListener {
                    state.value++
                }
            }
        },
        modifier = Modifier.padding(8.dp)
    )
    //widget.TextView
    AndroidView(factory = { ctx ->
        //Here you can construct your View
        TextView(ctx).apply {
            layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
        }
    }, update = {
        it.text = "You have clicked the buttons: " + state.value.toString() + " times"})}Copy the code

The bridge here is AndroidView, which is a Composable method:

@Composable
fun <T : View> AndroidView(
    factory: (Context) -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = NoOpUpdate
)
Copy the code

The factory takes a Context argument that builds a view. the update method is a callback, and the inflate is executed after that, as well as when the read state value changes.

Use XML layout in Compose

The aforementioned approach of using AndroidView in Compose is fine for a small amount of UI. What if you need to reuse an existing XML layout? Fear not, the View Binding is here.

It’s also easy to use:

  • First you need to turn on the View Binding.
buildFeatures {
    compose true
    viewBinding true
}
Copy the code
  • Second, you need an XML layout, such as acomplex_layout.
  • Then add a Compose View Binding dependency:androidx.compose.ui:ui-viewbinding.

Then build, generate the Binding class, and there you go.

@Composable
private fun ComposableFromLayout(a) {
    AndroidViewBinding(ComplexLayoutBinding::inflate) {
        sampleButton.setBackgroundColor(Color.GRAY)
    }
}
Copy the code

The ComplexLayoutBinding is a class generated based on the layout name.

The Composable method of AndroidView is still called inside the AndroidViewBinding.

In Compose, display fragments

This scenario might sound a bit weird, because Compose’s design philosophy seems to be meant to say goodbye to Fragment. In Compose’s UI, find a place to display a Fragment.

But there are so many scenarios, you might actually run into them.

Fragments are added through the FragmentManager and require a layout container. To change the ViewBinding example above, add a fragmentContainer to the layout and click to show the Fragment:

Column(Modifier.fillMaxSize()) {
    Text("I'm a Compose Text!")
    Button(
        onClick = {
            showFragment()
        }
    ) {
        Text(text = "Show Fragment")
    }
    ComposableFromLayout()
}

@Composable
private fun ComposableFromLayout(a) {
    AndroidViewBinding(
        FragmentContrainerBinding::inflate,
        modifier = Modifier.fillMaxSize()
    ) {

    }
}

private fun showFragment(a) {
    supportFragmentManager
        .beginTransaction()
        .add(R.id.fragmentContainer, PureComposeFragment())
        .commit()
}
Copy the code

There is no question of timing here, because clicking the button to display the Fragment drags the timing back. If you want to display the Fragment directly during initialization, you might throw an exception:

java.lang.IllegalArgumentException: No view found for id
Copy the code

Solutions:

@Composable
private fun ComposableFromLayout(a) {
    AndroidViewBinding(
        FragmentContrainerBinding::inflate,
        modifier = Modifier.fillMaxSize()
    ) {
        // here is safe
        showFragment()
    }
}
Copy the code

Show at least once in Your vehicle at Container View.

Theme & Style

For migrating the View app to Compose, you may need the Theme Adapter: github.com/material-co…

About using compose in view of the existing app: developer.android.com/jetpack/com…

conclusion

The combination of Compose and View relies primarily on two Bridges. It’s kind of interesting:

  • ComposeViewIt’s actually an Android View.
  • AndroidViewIt’s actually a Composable method.

Compose and View compatibility ensures that the project can be migrated gradually and safely, much like kotlin’s Java project migration. As for the learning curve, I don’t have enough experience. Anyway, I have to learn sooner or later.