» » Jetpack Compose - how to easily build a UI on Android

Jetpack Compose - how to easily build a UI on Android

In July of last year, along with Android Studio Arctic Fox, one of the long-awaited libraries, Jetpack Compose, was released. It allows you to create a declarative style user interface and promises to be a revolution in UI design.

We figure out if this is really the case, what are the advantages and disadvantages of the library. Details are in the article.

 

Benefits of Jetpack Compose

Jetpack Compose is a set of tools for developing UI in an Android application. It aims to speed up and simplify user interface development, get rid of redundant code, and combine the reactive programming model with the conciseness of Kotlin.

Right off the bat - what are the advantages of the library:

1. Less code. Jetpack Compose allows you to write less code, which means the developer can focus more on the problem, with less testing and debugging, and therefore bugs.

2. Intuitive. Compose uses a declarative API - the developer only needs to be told what to do, and everything else will fall on the shoulders of the library.

3. Ease of implementation. Compose is compatible with any existing code. For example, you can call Compose code from views (view) and, conversely, views from Compose. Many libraries like Jetpack Navigation, ViewModel and Coroutines are already adapted for Compose, which allows you to implement it in your code relatively quickly. In addition, Android Studio Arctic Fox supports previews of created views.

4. Has an extensive toolkit. Jetpack Compose lets you create beautiful apps with direct access to the Android Platform API and build-in support for Material Design, dark theme, animations and other cool stuff.

Next, let's go through the main aspects of the library and see how much the performance of the application improves.

Connecting to a project

To connect Jetpack Compose to a project, you need to include some lines of code in your build.gradle.

In the root, declare a variable with the Compose version:

ext {
   compose_version = '1.0.1'
}
В build.gradle модуля укажем следующие строки:
android {
   ...
   buildFeatures {
      compose true
   }
   composeOptions {
      kotlinCompilerExtensionVersion compose_version
      kotlinCompilerVersion '1.5.21'
   }
   ...
}
dependencies {
   implementation "androidx.compose.ui:ui:$compose_version"
   implementation "androidx.compose.material:material:$compose_version"
   implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
   implementation 'androidx.activity:activity-compose:1.3.1'
}

Here we indicate that we will use Jetpack Compose in the project and declare the necessary dependencies (you can read more about dependencies in the official guide ).

Further everything is simple. In the activity, we declare a Composable function, build a view hierarchy with the necessary attributes, and look at the result. 

Let's go through the code. I wrote two implementations of layouts of varying complexity:

1. Simple implementation

@Composable
fun Greeting(name: String) {
   Text(text = "Hello $name!")
}

Adds a TextView to the text layout with a concatenation of Hello and the argument passed to Greeting.

It's important to note that Composable function names start with a capital letter. This is a function naming convention, so if you write with lowercase, the studio will highlight the wrong naming.

2. More complex implementation

@Composable
fun ComplexComposeContent() {
   val scrollState = rememberScrollState()
   Column(
      modifier = Modifier.verticalScroll(scrollState)
         .padding(16.dp)
         .fillMaxSize()
   ) {
      Text(text = stringResource(id = R.string.article_title))
      Spacer(modifier = Modifier.height(16.dp))
      Image(painterResource(id = R.drawable.ic_atom), "atom", modifier = Modifier.wrapContentHeight())
      Spacer(modifier = Modifier.height(16.dp))
      Text(text = stringResource(id = R.string.text))
      Spacer(modifier = Modifier.height(16.dp))
      Button(onclick = {}, modifier = Modifier.fillMaxWidth()) {
         Text(text = stringResource(id = R.string.close_caption))
      }
   }
}

This option is a scrolling screen that contains an image, text, and a button. Let's look at some features:

  • You need to declare a Scroll State. Only not the usual one, but the one that allows you to save the state of the scroll through recomposition - rememberScrollState ().

  • Column is a ViewGroup with elements arranged vertically. 

  • Modifier allows you to manage attributes, add decorations and behavior to views.

The rest is intuitive. And this is just one of the key features of Jetpack Compose - even if you have not used the library before, you will still figure it out.

You can add views to an activity via extension setContent {}, for example:

override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContent {
   		Greeting("Android")
	}
}

In general, creating a UI looks really simple. Now let's determine how much the application is being optimized and how quickly the user will see the final screen. 

For testing, we will use the Jetpack Benchmark library, which, by the way, was also discussed in a separate article . The test code looks like this:

@RunWith(AndroidJUnit4::class)
class LayoutBenchmark {
   
   @get:Rule val activityRule = createAndroidComposeRule(MainActivity::class.java)
   
   @get:Rule val benchmarkRule = BenchmarkRule()
   
   @Test
   @UiThreadTest
   fun testSimpleResourceLayout() {
      activityRule.activity.let {
         benchmarkRule.measureRepeated {
            it.setSimpleResourceContent()
         }
      }
   }
   
   @Test
   @UiThreadTest
   fun testSimpleViewLayout() {
      activityRule.activity.let {
         benchmarkRule.measureRepeated {
            it.setSimpleViewContent()
         }
      }
   }
   
   @Test
   @UiThreadTest
   fun testSimpleComposableLayout() {
      activityRule.activity.let {
         benchmarkRule.measureRepeated {
            it.setSimpleComposableContent()
         }
      }
   }
…
}

Let's test three versions of installing a view in an activity:

  1. When passing a resource to setContentView.

  2. When passing a view to setContentView.

  3. With Composable function.

The results of testing can be viewed in the table: the left column is the name of the test, the right one is the time to complete:

Test

Test time

LayoutBenchmark.simpleResourceLayout

84 291 ns 

LayoutBenchmark.complexResourceLayout

964 792 ns

LayoutBenchmark.simpleViewLayout

2 481 ns 

LayoutBenchmark.complexViewLayout

126 024 ns 

LayoutBenchmark.simpleComposableLayout

94 ns

LayoutBenchmark.complexComposableLayout

88 ns

The conclusion is simple - the Composable function works several times faster than each of the previous options. So a quick display of the UI to the user is added to the treasury of benefits.

Instead of a conclusion

There is a drawback of the library, but it is very subjective - if the decomposition is incorrect and the screen has a large nesting, then the function will grow into an unpresentable form. And such code is already more difficult to maintain. Perhaps this is where the cons end.

Jetpack Compose is a new and ambitious tool that allows you to describe the user interface in a declarative and understandable format. Its definitely worth a try.

Related Articles

Add Your Comment

reload, if the code cannot be seen

All comments will be moderated before being published.