This article is for the 5th day of the UniFa developer blog Advent Calendar 2021.
Hello, this is Shakil from Product Engineering Department.
In this blog I am going to talk about the very basics of Jetpack Compose and why it is a good alternative to UI creation compared to traditional XML.
Jetpack Compose is Android’s modern toolkit for building native UI and is to replace XML in the future. It allows for really fast and simplified UI development with a lot less code by means of declarative functions a.k.a Composables.
I am not going to talk about how to integrate Compose to your project now but if you would like to know how, you could go here.
What is Declarative UI
Declarative UI is a design paradigm that allows developers to design the user interface based on data received and makes use of one programming language to create an entire application.
Examples of such frameworks include Flutter, React native, SwiftUI and Jetpack compose.
Why use Jetpack Compose?
- We can directly create UI in Kotlin, along with all the business logic. No XML required.
- With Compose, we can achieve same results as XML with lesser lines of code. Hence lesser to test, maintain and understand.
- Very easy to create different kinds of animations.
- It significantly reduces the app size because we have to write lesser code.
- Single source of truth for UI states.
- For more you can read this.
Creating UI with Compose
In the traditional Android development, we used to use different kinds of Layouts like RelativeLayout, LinearLayout, FrameLayout, ConstraintLayout, etc for creating UI elements each with their own perks and complexities. Jetpack Compose right now offers a much simpler way to to create UI with simple elements like:
- Row
- Column
- Box
Compose also has support for ConstraintLayout, but implementing it is not trivial and does not offer greater simplicity compared to XML. However, it is quite unlikely that we will need it when working with Compose as the three elements mentioned above works just as well if not better when creating any type of UI.
Row and Column
Row as its' name suggests is used to place components horizontally on the screen. Similarly, Column is used to place components vertically on the screen. Column & Row are Composable inline functions with arguments: Modifier, horizontalArrangement, verticalAlignment, and content. Except content, all the other arguments are optional. Let’s a look at the Row and Column integrated together below:
@Composable fun ColumnRowExample(){ Column( modifier = Modifier .fillMaxWidth() // Takes up max with it can inside its' parent .background(Color.White), // Color class here is different from COLOR class we are used to ) { Row( modifier = Modifier.fillMaxWidth().background(Color.Magenta), ) { Text( text = "無駄。無駄。無駄。。。。", style = MaterialTheme.typography.h6, textAlign = TextAlign.Left, modifier = Modifier.weight(2f).padding(16.dp) ) Text( text = "DIO", style = MaterialTheme.typography.h6, textAlign = TextAlign.Right, modifier = Modifier.weight(1f).padding(16.dp) ) } Row( modifier = Modifier.fillMaxWidth().background(Color.LightGray), ) { Text( text = "おら。おら。おら。。。。", style = MaterialTheme.typography.h6, textAlign = TextAlign.Left, modifier = Modifier.weight(2f).padding(16.dp) ) Text( text = "承太郎", style = MaterialTheme.typography.h6, textAlign = TextAlign.Right, modifier = Modifier.weight(1f).padding(16.dp) ) } } }
The output looks like the following:
Box
Box is kind of similar to RelativeLayout. Children in the Box will be stacked on top of one another unless we use an alignment modifier to specify where the composable should be drawn. Following shows an example:
@Composable fun BoxExample(){ Box( modifier = Modifier.fillMaxWidth() .background(Color.Yellow), ) { Text( text = "夢があります。", style = MaterialTheme.typography.h6, modifier = Modifier.align(Alignment.TopStart).padding(16.dp) ) Text( text = "MLK", style = MaterialTheme.typography.h6, modifier = Modifier.align(Alignment.TopEnd).padding(16.dp) ) } }
The output looks like the following:
Although this was just a brief overview, we can basically create almost any kind of UI using a combination of just the Rows, Columns and Boxes and using the Modifier argument to change their look and feel.
Modifiers
This object is used to define properties for our composable and makes use of the builder pattern. This means that changes are applied in the order of which they were set in the modifier object.
How about Displaying Long Lists
Currently in order to display long lists we use RecyclerView and have to create UI for each element in the list in a separate XML file and need to create Adapters and what not. In short displaying long list currently can be quite cumbersome involving multiple file and a lot of lines of code. Compose greatly simplifies this with LazyColumn & LazyRow.
Following shows an Example:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // We should never create and populate lists in Activity or Fragment // However I am just using this as an example, so should be Ok :D val list = mutableListOf<String>() for (i in 1..500) { list.add("This is item $i") } setContent { LazyColumnExample(list.toList()) } } @Composable private fun LazyColumnExample(list: List<String>) { LazyColumn { itemsIndexed(list) { _, content -> Text( text = content, fontSize = 32.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Left, modifier = Modifier .fillMaxWidth() .padding(vertical = 14.dp) ) } } }
The output looks like the following:
Wasn't that really simple. We no longer have to deal with Adapters (Yay! 😁 ). While the example above used a Text Composable for simplicity, we can definitely use our own custom composable as well.
Conclusion
This was just a brief overview of Compose. Hope you found it somewhat helpful. If I can, I would like to write more about Compose in the future regarding States, Animation, Pagination, Navigation, etc.