Jetpack You Know I Had to Do It to Them
Lesson i: Composable functions
Jetpack Etch is built effectually composable functions. These functions let yous define your app'southward UI programmatically past describing how it should look and providing data dependencies, rather than focusing on the procedure of the UI'due south construction (initializing an element, attaching information technology to a parent, etc.). To create a composable office, just add the @Composable annotation to the role name.
Add together a text element
To brainstorm, download the most recent version of Android Studio, and create an app using the Empty Etch Activity template. The default template already contains some Compose elements, simply permit'due south build information technology upwards step past step.
First, we'll brandish a "Hello earth!" text past adding a text element inside the onCreate method. You do this past defining a content cake, and calling the Text() part. The setContent block defines the activity'due south layout where we call composable functions. Composable functions tin can only exist called from other composable functions.
Jetpack Etch uses a Kotlin compiler plugin to transform these composable functions into the app'due south UI elements. For example, the Text() part that is defined by the Compose UI library displays a text label on the screen.
grade MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello globe!") } } } prove preview
Define a composable part
To make a function composable, add the @Composable annotation. To effort this out, define a MessageCard() function which is passed a name, and uses it to configure the text element.
grade MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } show preview
Preview your function in Android Studio
Android Studio lets you lot preview your composable functions inside the IDE, instead of installing the app to an Android device or emulator. The composable part must provide default values for any parameters. For this reason, you can't preview the MessageCard() function directly. Instead, let'due south make a second function named PreviewMessageCard(), which calls MessageCard() with an appropriate parameter. Add the @Preview annotation before @Composable.
@Composable fun MessageCard(proper name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") } show preview
Rebuild your project. The app itself doesn't change, since the new PreviewMessageCard() role isn't called anywhere, but Android Studio adds a preview window. This window shows a preview of the UI elements created by composable functions marked with the @Preview annotation. To update the previews at any fourth dimension, click the refresh button at the peak of the preview window.
grade MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Parcel?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } } show preview
course MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(proper name: Cord) { Text(text = "Hello $name!") } prove preview
@Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") } prove preview
Lesson 2: Layouts
UI elements are hierarchical, with elements contained in other elements. In Compose, you build a UI hierarchy past calling composable functions from other composable functions.
Add multiple texts
So far we've built our showtime composable function and preview! To detect more Jetpack Compose capabilities, we're going to build a elementary messaging screen containing a list of messages that tin be expanded with some animations.
Allow's start by making our bulletin composable richer by displaying the name of its author and a message content. We need outset to alter our composable parameter to accept a Message object instead of a String, and add some other Text composable within the MessageCard composable. Make certain to update the preview every bit well:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val writer: Cord, val torso: String) @Composable fun MessageCard(msg: Message) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!") ) } evidence preview
This code creates ii text elements inside the content view. However, since we haven't provided whatsoever information nigh how to arrange them, the text elements are fatigued on top of each other, making the text unreadable.
Using a Column
The Cavalcade part lets y'all suit elements vertically. Add Column to the MessageCard() function.
You can use Row to arrange items horizontally and Box to stack elements.
@Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } } show preview
Add together an epitome element
Let's enrich our message card by calculation a contour picture of the sender. Employ the Resource Managing director to import an paradigm from your photo library or use this ane. Add together a Row composable to take a well structured design and an Image composable inside it:
@Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact contour film", ) Column { Text(text = msg.writer) Text(text = msg.body) } } } show preview
Configure your layout
Our message layout has the correct structure but its elements aren't well spaced and the image is as well big! To decorate or configure a composable, Etch uses modifiers. They allow you to modify the composable's size, layout, appearance or add high-level interactions, such as making an element clickable. Yous can concatenation them to create richer composables. Permit's use some of them to improve the layout:
@Composable fun MessageCard(msg: Bulletin) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile film", modifier = Modifier // Ready image size to 40 dp .size(xl.dp) // Prune paradigm to be shaped every bit a circle .clip(CircleShape) ) // Add a horizontal space betwixt the image and the column Spacer(modifier = Modifier.width(eight.dp)) Column { Text(text = msg.author) // Add together a vertical space between the author and message texts Spacer(modifier = Modifier.meridian(4.dp)) Text(text = msg.body) } } } bear witness preview
form MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val author: Cord, val trunk: String) @Composable fun MessageCard(msg: Bulletin) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Bulletin("Colleague", "Hey, have a look at Jetpack Etch, it's bang-up!") ) } show preview
@Composable fun MessageCard(msg: Message) { Cavalcade { Text(text = msg.writer) Text(text = msg.torso) } } evidence preview
@Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", ) Cavalcade { Text(text = msg.author) Text(text = msg.trunk) } } } show preview
@Composable fun MessageCard(msg: Message) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile moving-picture show", modifier = Modifier // Gear up prototype size to 40 dp .size(twoscore.dp) // Clip image to be shaped as a circle .clip(CircleShape) ) // Add a horizontal space betwixt the image and the cavalcade Spacer(modifier = Modifier.width(8.dp)) Column { Text(text = msg.author) // Add a vertical space between the author and bulletin texts Spacer(modifier = Modifier.acme(4.dp)) Text(text = msg.body) } } } show preview
Lesson 3: Material Pattern
Compose is built to back up Textile Design principles. Many of its UI elements implement Textile Blueprint out of the box. In this lesson, yous'll style your app with Material Design widgets.
Use Material Design
Our bulletin design at present has a layout, but it doesn't look too expert yet.
Jetpack Compose provides an implementation of Material Design and its UI elements out of the box. We'll improve the appearance of our MessageCard composable using Fabric Design styling.
To start, we wrap our MessageCard function with the Material theme created in your project, ComposeTutorialTheme in this instance. Exercise it both in the @Preview and in the setContent function.
Material Design is built around three pillars: Colour, Typography, Shape. Let's add together them one by one
Note: the Empty Compose Activity generates a default theme for your project that allows you to customize MaterialTheme. If y'all named your project anything unlike from ComposeTutorial, you tin can find your custom theme in the ui.theme packet.
form MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { MessageCard(Message("Android", "Jetpack Compose")) } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { MessageCard( msg = Message("Colleague", "Take a look at Jetpack Compose, it'due south great!") ) } } show preview
Color
Styling with colors from the wrapped theme is like shooting fish in a barrel, and you lot tin can use values from the theme anywhere a color is needed.
Let's manner the title and add a border to the image:
@Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .prune(CircleShape) .border(one.5.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant ) Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } } show preview
Typography
Material Typography styles are available in the MaterialTheme, simply add them to the Text composables.
@Composable fun MessageCard(msg: Bulletin) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(forty.dp) .clip(CircleShape) .edge(1.five.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Cavalcade { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, mode = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.height(4.dp)) Text( text = msg.trunk, way = MaterialTheme.typography.body2 ) } } } prove preview
Shape
With Shape nosotros can add together the final touches. We as well add together padding to the message for a better layout.
@Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = zippo, modifier = Modifier .size(40.dp) .clip(CircleShape) .edge(1.v.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(viii.dp)) Column { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.acme(4.dp)) Surface(shape = MaterialTheme.shapes.medium, height = 1.dp) { Text( text = msg.body, modifier = Modifier.padding(all = iv.dp), style = MaterialTheme.typography.body2 ) } } } } show preview
Enable dark theme
Dark theme (or dark manner) can be enabled to avert a bright display especially at dark, or simply to save the device battery. Thanks to the Material Design support, Jetpack Etch tin can handle the nighttime theme by default. Having used Material Design colors, text and backgrounds will automatically adapt to the dark background.
You tin create multiple previews in your file as separate functions, or add multiple annotations to the same part.
Let's add a new preview annotation and enable night mode.
@Preview(proper name = "Low-cal Way") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = truthful, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { MessageCard( msg = Bulletin("Colleague", "Hey, accept a look at Jetpack Compose, it's neat!") ) } } show preview
Color choices for the light and dark themes are divers in the IDE-generated Theme.kt file.
So far, we've created a message UI element that displays an image and 2 texts with different styles, and it looks good both in light and dark themes!
@Preview(proper noun = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, proper name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { MessageCard( msg = Bulletin("Colleague", "Hey, take a look at Jetpack Compose, it's bully!") ) } } show preview
course MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { MessageCard(Message("Android", "Jetpack Etch")) } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { MessageCard( msg = Message("Colleague", "Have a look at Jetpack Compose, information technology'due south great!") ) } } show preview
@Composable fun MessageCard(msg: Bulletin) { Row(modifier = Modifier.padding(all = eight.dp)) { Paradigm( painter = painterResource(R.drawable.profile_picture), contentDescription = cipher, modifier = Modifier .size(forty.dp) .clip(CircleShape) .border(ane.5.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant ) Spacer(modifier = Modifier.height(iv.dp)) Text(text = msg.body) } } } show preview
@Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Epitome( painter = painterResource(R.drawable.profile_picture), contentDescription = cipher, modifier = Modifier .size(xl.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.acme(4.dp)) Text( text = msg.body, style = MaterialTheme.typography.body2 ) } } } testify preview
@Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = zero, modifier = Modifier .size(xl.dp) .clip(CircleShape) .border(i.5.dp, MaterialTheme.colors.secondary, CircleShape) ) Spacer(modifier = Modifier.width(viii.dp)) Cavalcade { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.superlative(4.dp)) Surface(shape = MaterialTheme.shapes.medium, acme = i.dp) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), manner = MaterialTheme.typography.body2 ) } } } } prove preview
@Preview(proper name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Night Style" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { MessageCard( msg = Bulletin("Colleague", "Hey, have a look at Jetpack Compose, information technology'due south swell!") ) } } show preview
Lesson 4: Lists and animations
Lists and animations are everywhere in apps. In this lesson, you will learn how Compose makes it easy to create lists and fun to add animations.
Create a list of messages
A chat with i bulletin feels a bit lonely, so let'south change our chat to have more than one message. Nosotros need to create a Chat function that volition show multiple messages. For this use instance, we can utilize Etch's LazyColumn and LazyRow. These composables render only the elements that are visible on screen, so they are designed to exist very efficient for long lists. At the aforementioned time, they avert the complexity of RecyclerView with XML layouts.
In this code snippet, y'all tin can run into that LazyColumn has an items child. It takes a List as a parameter and its lambda receives a parameter we've named message (we could take named it any we want) which is an instance of Message. In curt, this lambda is called for each particular of the provided List. Import this sample dataset into your projection to assistance bootstrap the conversation chop-chop.
import androidx.etch.foundation.lazy.items @Composable fun Conversation(letters: List<Message>) { LazyColumn { items(messages) { bulletin -> MessageCard(bulletin) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Chat(SampleData.conversationSample) } } prove preview
Animate messages while expanding
Our conversation is getting more interesting. Information technology'due south time to play with animations! Nosotros will add the ability to expand a bulletin to evidence a longer one, animating both the content size and the groundwork colour. To shop this local UI land, nosotros need to go on track of whether a bulletin has been expanded or not. To keep track of this state modify, we have to employ the functions remember and mutableStateOf.
Composable functions can store local land in memory by using remember, and track changes to the value passed to mutableStateOf. Composables (and their children) using this state will become redrawn automatically when the value is updated. Nosotros call this recomposition.
By using Compose's state APIs similar recall and mutableStateOf, whatever changes to state automatically update the UI.
Note: You will need to add together the following imports to correctly use past. Alt+Enter or Option+Enter adds them for you.
import androidx.compose.runtime.getValue import androidx.etch.runtime.setValue
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Package?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = eight.dp)) { Prototype( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape) ) Spacer(modifier = Modifier.width(viii.dp)) // We continue track if the bulletin is expanded or non in this // variable var isExpanded by recall { mutableStateOf(false) } // Nosotros toggle the isExpanded variable when nosotros click on this Cavalcade Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, colour = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, elevation = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the bulletin is expanded, we brandish all its content // otherwise we only display the commencement line maxLines = if (isExpanded) Int.MAX_VALUE else 1, way = MaterialTheme.typography.body2 ) } } } } prove preview
Now nosotros can alter the background of the bulletin content based on isExpanded when nosotros click on a message. Nosotros will employ the clickable modifier to handle click events on the composable. Instead of just toggling the background color of the Surface, we will animate the background color by gradually modifying its value from MaterialTheme.colors.surface to MaterialTheme.colors.master and vice versa. To practise then, we will use the animateColorAsState function. Lastly, nosotros will utilize the animateContentSize modifier to breathing the bulletin container size smoothly:
@Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Prototype( painter = painterResource(R.drawable.profile_picture), contentDescription = nil, modifier = Modifier .size(xl.dp) .clip(CircleShape) .border(1.v.dp, MaterialTheme.colors.secondaryVariant, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep runway if the message is expanded or non in this // variable var isExpanded past remember { mutableStateOf(fake) } // surfaceColor will be updated gradually from ane colour to the other val surfaceColor: Color by animateColorAsState( if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.height(iv.dp)) Surface( shape = MaterialTheme.shapes.medium, elevation = 1.dp, // surfaceColor color will be changing gradually from principal to surface color = surfaceColor, // animateContentSize will modify the Surface size gradually modifier = Modifier.animateContentSize().padding(1.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, nosotros display all its content // otherwise we but display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.body2 ) } } } } show preview
import androidx.compose.foundation.lazy.items @Composable fun Conversation(messages: List<Message>) { LazyColumn { items(letters) { message -> MessageCard(message) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Chat(SampleData.conversationSample) } } bear witness preview
grade MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Bulletin) { Row(modifier = Modifier.padding(all = viii.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.five.dp, MaterialTheme.colors.secondaryVariant, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We go along track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(imitation) } // We toggle the isExpanded variable when we click on this Column Cavalcade(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.writer, colour = MaterialTheme.colors.secondaryVariant, manner = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.height(iv.dp)) Surface( shape = MaterialTheme.shapes.medium, pinnacle = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise nosotros only display the showtime line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.body2 ) } } } } show preview
@Composable fun MessageCard(msg: Bulletin) { Row(modifier = Modifier.padding(all = viii.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = cipher, modifier = Modifier .size(forty.dp) .clip(CircleShape) .border(one.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep runway if the message is expanded or not in this // variable var isExpanded past remember { mutableStateOf(false) } // surfaceColor volition be updated gradually from one color to the other val surfaceColor: Color by animateColorAsState( if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colors.secondaryVariant, style = MaterialTheme.typography.subtitle2 ) Spacer(modifier = Modifier.peak(4.dp)) Surface( shape = MaterialTheme.shapes.medium, elevation = 1.dp, // surfaceColor colour will be changing gradually from primary to surface color = surfaceColor, // animateContentSize will change the Surface size gradually modifier = Modifier.animateContentSize().padding(one.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise nosotros only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else i, style = MaterialTheme.typography.body2 ) } } } } testify preview
Next steps
Congratulations, you've finished the Compose tutorial! You've congenital a simple chat screen efficiently showing a list of expandable & blithe messages containing an prototype and texts, designed using Material Design principles with a dark theme included and previews—all in under 100 lines of code!
Here'south what you've learned so far:
- Defining composable functions
- Adding dissimilar elements in your composable
- Structuring your UI component using layout composables
- Extending composables by using modifiers
- Creating an efficient list
- Keeping runway of country and modifying information technology
- Adding user interaction on a composable
- Animative letters while expanding them
If you desire to dig deeper on some of these steps, explore the resource beneath.
Go along your learning
Source: https://developer.android.com/jetpack/compose/tutorial
Post a Comment for "Jetpack You Know I Had to Do It to Them"