paint-brush
Practical Tips for Refactoring on Composeby@leonidivankin
610 reads
610 reads

Practical Tips for Refactoring on Compose

by Leonid IvankinJuly 22nd, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Refactoring is an Android application that needs to be refactored on Compose. Refactoring by itself does not degrade performance compared to the original XML approach with a careful approach. In complex projects with thousands of files, refactoring will simply not be applicable. Refactor the inner part first, and leave the outer part (red and purple) in the XML. Apply ComposeView. Refactor red and purple rectangles to stay on the XML, and the green and orange.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Practical Tips for Refactoring on Compose
Leonid Ivankin HackerNoon profile picture


Introduction

Very often it happens that an Android application needs to be refactored on Compose. But the project is very big and it is impossible to do it in one go. In this case, Compose has classes for almost seamless implementation of Compose into XML and vice versa.


The full documentation is available here. In this article I won't dwell on the theoretical part, I want to share my practice and tips about refactoring.


Observations and Assumptions

  • The refactoring will be shown on the example of a simple project;
  • It is assumed that compose is already attached to your project. If it is not, see the documentation.


Selecting an example

Let's imagine that we have a project with LegacyActivity. The LegacyActivity has 4 nested rectangles (FrameLayout) in red, purple, green, and orange. This view is implemented using XML. The full code is here:

https://gist.github.com/acf616d147f50b6d6d2ecf742a74dda3

https://gist.github.com/aa0e79dbe447027b030ff558eec91884


At some point, we are faced with the task of refactoring this view on Compose. My example above is trivial enough. But let's imagine that our visual elements are so huge that we can't refactor all the visual representations in a reasonable amount of time.


At the same time, we can't completely abandon refactoring either. We decide to refactor only part of this view in the first iteration. We can go two ways:

  1. Refactor the inside first (green and orange rectangles on compose). Leave the outer part (red and purple) in the XML. Apply ComposeView.
  2. Refactor the outer part first (red and purple). Leave the inner part in the XML. Apply AndroidView


Google documentation says that there is not much difference in these approaches. For my part, I want to note that most often your project will tell you which way to go. In complex projects with thousands of files, very often one of these approaches will simply not be applicable.


I also want to note that refactoring by itself does not degrade performance compared to the original XML approach with a careful approach (see the article).


Refactor the inner part first

We need the red and purple rectangles to stay on the XML, and the green and orange to be on Compose.



The full result of the refactoring is here:

https://gist.github.com/b4ea2806486dc441ff8004d2acae49b5

https://gist.github.com/f847856cef5529cc7c83538f2c5499b6


In order to achieve this, it is necessary:

  1. Create a part on Compose. In my example it is an InternalPart:


@Composable
private fun InternalPart() {
   Box(
       Modifier
           .fillMaxSize()
           .background(color = Color(0xFF4CAF50))
           .padding(32.dp)
   ) {
       Box(
           Modifier
               .fillMaxSize()
               .background(color = Color(0xFFFF9800))
               .padding(32.dp)
       )
   }
}


  1. Remove unnecessary parts from the XML. In my example, it is 2 internal FrameLayout.
  2. In their place add ComposeView. In my example it is:


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


  1. In LegacyActivity, find the necessary ComposeView. Call the setContent{} method and pass InternalPart() to it.


findViewById<ComposeView>(R.id.composeViewInternalPart).setContent {
   InternalPart()
}


  1. Run the application.

As you can see, the very fact of connecting compose to the XML is not a labor-intensive operation. Much more effort is required to refactor the existing view to compose.


Refactor the external part first

Let's try to solve the inverse problem: that the red and purple rectangles become on Compose, while the green and orange remain on XML.



The full code is here:

https://gist.github.com/ac78624f8d0bea5e0d580d07b9623e07

https://gist.github.com/a6f08666823c5be6d73b9c29549e191b


In order to achieve our goal, we need:

  1. In the XML, remove the outer part so that only the green and orange rectangles remain. In my case these are:


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#4CAF50"
   android:padding="32dp">

   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="#FF9800"
       android:padding="32dp" />
</FrameLayout>


Refactor the outer part of Compose. In my case these are the red and purple rectangles:


@Composable
private fun ExternalPart() {
   Box(
       Modifier
           .fillMaxSize()
           .background(color = Color(0xFFF44336))
           .padding(32.dp)
   ) {
       Box(
           Modifier
               .fillMaxSize()
               .background(color = Color(0xFF9C27B0))
               .padding(32.dp)
       ) {
          //...
       }
   }
}


  1. Add AndroidView() and in the factory parameter the rest of the view, which is obtained from the XML using the View.inflate() method:
AndroidView(
   modifier = Modifier.fillMaxSize(),
   factory = { context ->
       View.inflate(context, R.layout.internal_part, null)
   }
)


  1. Start the application.


As you can see, in this case, XML and compose also work almost seamlessly.

Comparison of results

After all the manipulation, I would recommend doing a design review - take the original and the resulting screenshots and compare the results, overlaying them on top of each other. There are many tools that allow you to do this. I mostly use Figma for such tasks.