1

Interoperability with Jetpack Compose [FREE]

 1 year ago
source link: https://www.raywenderlich.com/31473074-interoperability-with-jetpack-compose
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Home Android & Kotlin Tutorials

Interoperability with Jetpack Compose

Learn how to use Compose Interoperability in your Android app.

By Eric Duenes Jun 6 2022 · Article (15 mins) · Intermediate

Version

ComposeInteroperability-feature.png

You’re close to completing your android application, and it’s all written using Android Views. You want to start using JetPack Compose, but don’t want to rewrite all your layout.xml files. Or, maybe you started a project using Jetpack Compose, but want to leverage existing Android Views. What can you do?

You’re in luck — Jetpack Compose offers Interoperability. You can take advantage of this modern toolkit one composable at a time and migrate in and out of your Android Views at your own pace.

In this tutorial, you’ll explore Compose interoperability. You’ll use composables to complete a scorekeeper app that was created using Android Views. You’ll change the playing field and add Android Views to an activity using Jetpack Compose. As you complete this tutorial, you’ll learn about:

  • Jetpack Compose Interoperability APIs
  • Composable Lifecycles
  • Composition Strategy
  • Recomposition

The match is about to start — may the best team win.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Open the project with Android Studio. You’ll see the following file structure:

Project Setup

Project Setup

The AndroidManifest.xml declares .MainActivity as the activity to launch after the app starts, and it also contains .ComposeActivity — which currently does nothing. Using intent-filter attributes, you’ll be able to launch the main activity for the first half of the tutorial and the Compose activity for the second half. Here’s an overview of the two activities:

  • MainActivity.kt was developed using Android XML layout views and view binding. The activity inflates activity_main.xml layout view, and it provides the ability to keep score for the home team. These are the files you’ll be manipulating in the first half of the tutorial.
  • ComposeActivity.kt was developed using Jetpack Compose. It will leverage components from VisitorTeamView.kt and provides the ability to keep score for the home team. You’ll be using these files in the second part of the tutorial.

Sync the project. Then, build and run. The app will look like this:

Starting Screen for the Skeeper Application

Starting Screen for the Skeeper Application

The starter project gives your user the ability to keep score for the home team — but sports matches are usually played by two teams. Using Jetpack Compose interoperability, you’ll add functionality to keep score for the visiting team.

Introducing Recomposition Strategies

Before you start keeping score, consider the lifecycle of a composable.

A composable can go through many changes during the lifecycle of your application. It’s drawn on your screen when initialized and re-drawn whenever the state changes. Additionally, when using Interoperability API, a composable can become detached and disposed of Android Views at different stages of the view lifecycle. As a result, it’s best to identify and set the correct ViewCompositionStrategy.

By setting the correct view composition strategy, you’re ensuring two things:

  • Your components don’t become disposed of while still on the screen. That would be a bad user experience.
  • Your components do become disposed of at the right time. Retaining components unnecessarily will cause memory leaks in your application.

There are three view composition strategy options available:

  1. DisposeOnDetachedFromWindow: This is the default strategy, and the component will be disposed when the view is detached. This strategy can cause a memory leak if you call createComposition(). In this scenario, the view will get created but might never be attached, so it won’t have the opportunity to be disposed.
  2. DisposeOnLifecycleDestroyed: With this strategy, the component will be destroyed when the owner of the lifecycle is destroyed.
  3. DisposeOnViewTreeLifecycleDestroyed: In this strategy, the component will be destroyed when the parent’s lifecycle is destroyed. This strategy is ideal when your component is in a Fragment.

Adding Composables

From the starter project, open activity_main.xml. You’ll add the Visitor Team section using ComposeView.

After the divider view, add the following code:

<androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="40dp"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toBottomOf="@id/divider"
  />

You’ve just given yourself an entry point to add composables to your Android View.

Now, open MainActivity.kt and inside onCreate(), use view binding to apply the composable like this:

//1
binding.composeView.apply {
  //2
  setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
  //3
  setContent {
   //4
    val visitorScore = remember { mutableStateOf(0) }
    //5
    VisitorScreenView(
      visitorScore = visitorScore.value,
      decreaseScore = { visitorScore.value-- },
      increaseScore = { visitorScore.value++ })
  }
}

Score one for the good guys… yes, that’s a sports reference. :]

You’ve now applied VisitorScreenView composable to the Android View. Here’s what you did:

  1. Using view binding, you’re able to get a handle to ComposeView. Remember? You added that to your layout XML file.
  2. Here, you’re setting the composition strategy to DisposeOnDetachedFromWindow. This strategy is safe to use here since your composables will be displayed in an activity, and you won’t be calling createComposition() anywhere in your code.
  3. This is the block where you’ll add your composables.
  4. You’ll keep track of the score with visitorScore. This will be stored as state and will be used by VisitorScreenView. To learn more about preserving the UI state of an Android application, check out Managing State in Jetpack Compose.
  5. VisitorScreenView is a composable function that has already been created for you. It contains the view for the visiting team and takes three parameters:
    • visitorScore variable accepts an integer value that will be used to display the score for the visiting team.
    • decreaseScore parameter accepts a lambda function. It will be used to decrease the visiting team score.
    • increaseScore parameter accepts a lambda function. It will be used to increase the visiting team’s score.

Build and run the application. You’ll see this screen, and you’ll be able to modify the visiting team’s score.

Added Visitor View to Skeeper App

Added Visitor View to Skeeper App

Abstracting Composables

You’ve now successfully added composables to your view, and you’re keeping score like a champ. But as the game progresses, you realize something… you can track the score, but with your Android Views and composables in the same file, how can you track where Android Views end and composables begin?

To keep things clean, you should separate your composables from your Android Views. You can use AbstractComposeView to achieve this.

Open VisitorTeamView.kt and add the following code:

class VisitorTeamView @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null,
  defStyleAttr: Int = 0
  ) : AbstractComposeView(context, attrs, defStyleAttr) {
  
  //1
  private val visitorScore =  mutableStateOf(0)

  //2
  @Composable
  override fun Content() {
    setViewCompositionStrategy(
      ViewCompositionStrategy.DisposeOnDetachedFromWindow )

    VisitorScreenView(
      visitorScore = visitorScore.value,
      decreaseScore = { visitorScore.value-- },
      increaseScore = { visitorScore.value++ })

  }
}

VisitorTeamView class implements AbstractComposeView. The code should look familiar to you because it’s similar to what you implemented in the previous step.

  1. Your visitorScore variable will hold the score value as state.
  2. Inside Content() is where you’ll set your composition strategy and call your composable functions.
Note: There’s no need to use view binding because you’ll call the VisitorTeamView class directly from the layout file.

Open activity_main.xml and replace androidx.compose.ui.platform.ComposeView declaration with com.yourcompany.skeeper.ui.VisitorTeamView:

<com.yourcompany.skeeper.ui.VisitorTeamView
  android:id="@+id/compose_view"
  ...
/>

Finally, to complete the cleanup, go back to MainActivity and remove binding.composeView.apply {} along with all the lines of code inside of it.

There you have it. All your composables are in the com.yourcompany.skeeper.ui package.

Build and run your application to see that it looks and behaves the same way.

Switching to Composable UI

At the halftime of a game, the teams usually switch sides to keep a fair playing field. Now, you’ll change the playing field as well — and this time, you’ll integrate Android Views into your composables. Within the same Skeeper app, you’ll find ComposeActivity.kt, and it’s written purely in Compose. This UI only has the Home View completed. You’ll complete the visitors section using Android Views.

Set up the Skeeper app to start with ComposeActvity. To do this, open AndroidManifest.xml and uncomment intent-filter of .ComposeActivity. Next, you’ll comment out intent-filter of .MainActivity. The next time you run your application, it will display ComposeActivity.

Your Manifest file will look like this:

...
<!-- Main Activity -->
    <activity
        android:name="com.yourcompany.skeeper.MainActivity"
        android:exported="true"
        android:theme="@style/SplashTheme">
      //1
      <!--
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      -->
    </activity>

    <!-- Compose Activity -->
    <activity
        android:name="com.yourcompany.skeeper.ComposeActivity"
        android:exported="true"
        android:theme="@style/AppTheme.NoActionBar" >
      //2
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  1. By commenting out intent-filter, the main activity will no longer be launched when the application starts.
  2. By uncommenting intent-filter, the Compose activity will be launched when the application starts.

Build and run your application to see that ComposeActivity is launched when the application is started.

Your application will look like this, and you’ll only be able to change the home team’s score.

Starting Screen

Starting Screen for the Skeeper Application

Composing Android Views

Two APIs give you the ability to add Android Views into Compose Views:

  1. AndroidView
  2. AndroidViewBinding

You’ll implement AndroidViewBinding — but before you do that, here’s an overview of AndroidView.

Reviewing AndroidView

In the same way that you would add a Button or Text composable to your user interface, you can add AndroidView composable. This is a special type of composable that has three things: a modifier, factory and update. Here’s an example:

AndroidView(
  modifier = Modifier.fillMaxSize(), 
  //1
  factory = { context ->
    MyView(context).apply {
      // Set listeners here
    }
  },
  //2
  update = { view ->
     //Recomposed when state changes
  }
)
  1. The factory block will be executed only once and will run on the UI Thread. Use the factory to perform initializations such as onClickListener.
  2. The update block is the set of code that will be recomposed when state changes. Add your UI components there.

If you have an XML layout file containing your Android View, it’s much easier to inflate it within a composable.

Implementing AndroidViewBinding

This API allows you to use view binding and simply inflate an existing XML layout file.

Wouldn’t you know it, the Skeeper app conveniently has visitor_team_xml_view.xml. This layout file uses Android Views and contains the visitors view. Take a glance at it and see that it has a couple of TextViews and Buttons.

Now, open ComposeActivity.kt and add a composable function called VisitorTeamXmlView.

@Composable
fun VisitorTeamXmlView(visitorScore: Int, decreaseScore:  () -> Unit, increaseScore:  () -> Unit){
  //1
  AndroidViewBinding(VisitorTeamXmlViewBinding::inflate){
    //2
    visitorScoreText.text = visitorScore.toString()
    decrementVisitorButton.setOnClickListener { decreaseScore() }
    incrementVisitorButton.setOnClickListener { increaseScore() }
  }
}
  1. VisitorTeamXmlView will use AndroidViewBinding API to inflate your layout file.
  2. You can set clickListeners and any other attributes you need inside AndroidViewBinding declaration.

The last thing left to do is call VisitorTeamXmlView composable function to display your layout.

In ComposeActivity, find setContent() where HomeTeamComposeView and Divider composables are being called.

Call the VisitorTeamXmlView function:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContent {
    ...
    VisitorTeamXmlView(visitorScore = visitorScore.value,
                decreaseScore = { visitorScore.value-- },
                increaseScore = { visitorScore.value++ })
  }
}

Build and run the application to see visitor_team_xml_view.xml get inflated and rendered alongside your composables.

Added Visitor View to Skeeper App

Added Visitor View to Skeeper App

Score one more for the good guys!

Where to Go From Here?

Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

In this tutorial, you learned how to:

  • Identify a recomposition strategy
  • Add composables to Android Views
  • Add Android Views to composables

Interoperability with Jetpack Compose gives you a lot of flexibility with your user interface. If you want to learn more about Jetpack Compose, check out Jetpack Compose Tutorial for Android: Getting Started or Jetpack Compose by Tutorials.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!

raywenderlich.com Weekly

The raywenderlich.com newsletter is the easiest way to stay up-to-date on everything you need to know as a mobile developer.

Get a weekly digest of our tutorials and courses, and receive a free in-depth email course as a bonus!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK