Jetpack Compose foresight

  • JetPack posepose1.0.0-Beta01 has recently been officially released, which makes the majority of Android developers join the journey of learning, and Google’s Android Development Challenge has brought a large number of developers who are learning JetPack Compose. At one point, it became the hot UI kit of the moment.

  • Jetpack Compose is a modern toolkit for building native Android uis that simplify and speed up UI development on Android with less code, powerful tools, and the intuitive KotlinAPI.

Welcome to fork the GitHub repository, which is also the template provided by the official challenge project

Official template User this Templete

This article demo code run environment

  • Android Studio version: Android Studio Arctic Fox >>> 2020.3.1 Canary 9
  • JetpackCompose version: 1.0.0-beta01
  • The JDK version is 11.0.9

ps:

  1. For the template project to run properly, the JDK is greater than 11 and Android Studio version 4.3 or higher, which is Canary 9 Arctic Fox
  2. By the way, are there any plans to successfully run the template Project in the current Android Studio beta?

Study guide

In the following, I will start from Foundation, Layout, Material, Animation.

Foundation

  • Image
  1. Images are used to display images. It is similar to ImageView in the classic Android View system, and you can use painterResource to load resource files
@Composable
fun ImageResource() {
    val image: Painter = painterResource(R.drawable.lagotto_romagnolo)
    Image(painter = image, contentDescription = null)
}
Copy the code

2. The actual effect is as follows

  • Text
  1. Use Text to display Text. You can use the style parameter to define things like TextDecoration or FontFamily.
@composable @Preview Fun TextExample(){Column {Text("Jacky Tallow") Text("Text with cursive font", style = TextStyle(fontFamily = FontFamily.Cursive)) Text( text = "Text with LineThrough", style = TextStyle(textDecoration = TextDecoration.LineThrough) ) Text( text = "Text with underline", style = TextStyle(textDecoration = TextDecoration.Underline) ) Text( text = "Text with underline, linethrough and bold", style = TextStyle( textDecoration = TextDecoration.combine( listOf( TextDecoration.Underline, TextDecoration.LineThrough ) ), fontWeight = FontWeight.Bold ) ) } }Copy the code
  1. Common words
 Text("Jacky Tallow")
Copy the code
  1. Cursive writing
 Text("Text with cursive font", style = TextStyle(fontFamily = FontFamily.Cursive))
Copy the code
  1. Text with LineThrough
Text(
            text = "Text with LineThrough",
            style = TextStyle(textDecoration = TextDecoration.LineThrough)
        )
Copy the code
  1. Underlined text
        Text(
            text = "Text with underline",
            style = TextStyle(textDecoration = TextDecoration.Underline)
        )
Copy the code
  1. Underlined, bold and straight text
  Text(
            text = "Text with underline, linethrough and bold",
            style = TextStyle(
                textDecoration = TextDecoration.combine(
                    listOf(
                        TextDecoration.Underline,
                        TextDecoration.LineThrough
                    )
                ), fontWeight = FontWeight.Bold
            )
        )
Copy the code
  • BaseTextFiled

BaseTextField can be used to insert text.

@Composable
fun BaseTextFieldDemo() {
    var textState by remember { mutableStateOf(TextFieldValue("h")) }

    Column {
        TextField(value = textState, onValueChange = {
            textState = it
        })
        Text("The textfield has this text: " + textState.text)
    }

}
Copy the code
  • Canvas

Needless to say, as a canvas, you can draw a variety of graphics to beautify the interface. Here is an example of this code

@composable @preview (showBackground = true) fun CanvasDemo() {Canvas(Modifier = modifier.fillMaxSize()) {// Draw rectangle DrawRect (color.blue, topLeft = Offset(0f, 0f), size = size (this.size.width, 55f)) drawRect(color.blue, topLeft = Offset(0f, 0f), size = size (this.size.width, 55f)) Center = Offset(50f, 200f), radius = 40f) drawLine(color.green, Offset(20f, 0f), Offset(200f, 200f) StrokeWidth = 5f) // Draw pie drawArc(color.black, 0f, 60f, useCenter = true, size = size (300f, 300f) topLeft = Offset(60f, 60f) ) } }Copy the code
  1. Use drawRect to draw a rectangle, drawCircle to draw a circle, drawLine to draw a straight line, and drawArc to draw a pie, respectively

  2. The actual effect is as follows

  • Shape

Shape makes a Composable of a particular Shape

  1. The following uses an example to draw a round shape, a rounded shape, a rectangular shape, and a round shape with cut corners
@Composable @Preview fun PreviewShapeDemo() { Column(modifier = Modifier .fillMaxWidth() .wrapContentSize(Alignment.center)) {// Rectangles ExampleBox(shape = RectangleShape) // Shapes ExampleBox(shape = CircleShape) // Rectangle shape with rounded corners ExampleBox(shape = RoundedCornerShape(8.dp)) // Rectangle shape with cut corners ExampleBox(shape = CutCornerShape(10.dp))}} // Shape @Composable fun ExampleBox(shape: Shape) { Box(modifier = Modifier .size(100.dp) .clip(shape) .background(Color.Red)) }Copy the code
  1. So how do you draw custom shapes?

In general, there are general shape and extended shape interfaces

  • General shape

GenericShape. Let’s see how the triangle is drawn.

private val TriangleShape = GenericShape { size ->
    // 1)
    moveTo(size.width / 2f, 0f)

    // 2)
    lineTo(size.width, size.height)

    // 3)
    lineTo(0f, size.height)
}
Copy the code
  • Inside GenericShape, you can draw custom shapes. You can access the Size Object. This is the Composable size of the application shape. You can use height size.height and width size.width
  • Initially, the painter will start at the top left of the parent object composable (0x, 0y). Use moveTo () to set the painter’s coordinates. In this case, the coordinates will be set to half the width of the parent layout, and the coordinates will be 0y.
  • This will draw a line from the artist coordinates set in 1) to the lower right corner of the parent layout. Then set the artist coordinates to this corner automatically.
  • This will draw a line in the lower left corner. GenericShape implicitly executes the close () – function. Close () will draw a line from the last plotted coordinate to the first defined coordinate.
  • Extended shape interface

Let’s take a look at this example

/** * Defines a generic shape. */ interface Shape { /** /** * Creates [Outline] of this shape for the given [size]. * * @param size the size of the shape boundary. * @param density the current density of the screen. * * @return [Outline] of  this shape for the given [size]. */ fun createOutline(size: Size, density: Density): Outline }Copy the code

You can extend the Shape interface to create your own implementation of Shape. Inside createOutline, you can get the size of the composable object (to which the shape is applied) and the density of the screen. You must return an instance of Outline. Outline is a sealed class with the following subclasses:

  • (val rect: rect)
  • Round (val rrect: rrect)
  • Generic (Val path: path)
class CustomShape : Shape {
    override fun createOutline(size: Size, density: Density): Outline {
        val path = Path().apply {
            moveTo(size.width / 2f, 0f)
            lineTo(size.width, size.height)
            lineTo(0f, size.height)
            close()
        }
        return Outline.Generic(path)
    }
}
Copy the code
  • LazyColumn

LazyColumn is designed as a vertically scrolling list that only consists of, and sketches out, currently visible items, similar to the Recyclerview in the classic Android View system

  1. Here’s an example, using a simple LazyColumn
@Composable fun LazyColumnDemo() { val list = listOf( "A", "B", "C", "D" ) + ((0.. 100).map { it.toString() }) LazyColumn(modifier = Modifier.fillMaxHeight()) { items(items = list, itemContent = { item -> Log.d("COMPOSE", "This get rendered $item") when (item) { "A" -> { Text(text = item, style = TextStyle(fontSize = 80.sp)) } "B" -> { Button(onClick = {}) { Text(text = item, style = TextStyle(fontSize = 80.sp)) } } "C" -> { //Do Nothing } "D" -> { Text(text = item) } else -> { Text(text = item, style = TextStyle(fontSize = 80.sp)) } } }) } }Copy the code
  • LazyRow

A LazyRow is a horizontally scrolling list that consists of, and Outlines, only the items that are currently visible. It is similar to the horizontal Recyclerview in the classic Android View system.

  1. Here’s an example, simple use of LazyRow
@Composable fun LazyRowDemo() { val list = listOf( "A", "B", "C", "D" ) + ((0.. 100).map { it.toString() }) LazyRow(modifier = Modifier.fillMaxHeight()) { items(items = list, itemContent = { item -> Log.d("COMPOSE", "This get rendered $item") when (item) { "A" -> { Text(text = item, style = TextStyle(fontSize = 80.sp)) } "B" -> { Button(onClick = {}) { Text(text = item, style = TextStyle(fontSize = 80.sp)) } } "C" -> { //Do Nothing } "D" -> { Text(text = item) } else -> { Text(text = item, style = TextStyle(fontSize = 80.sp)) } } }) } }Copy the code

Layout

  • Box

As a Box layout, boxes are stacked on top of each other, and you can use the align modifier to specify where you should draw composable objects

  1. Take a look at the example below
fun BoxExample() {
    Box(Modifier.fillMaxSize()) {
        Text("This text is drawn first", modifier = Modifier.align(Alignment.TopCenter))
        Box(
            Modifier.align(Alignment.TopCenter).fillMaxHeight().width(
                50.dp
            ).background( Color.Blue)
        )
        Text("This text is drawn last", modifier = Modifier.align(Alignment.Center))
        FloatingActionButton(
            modifier = Modifier.align(Alignment.BottomEnd).padding(12.dp),
            onClick = {}
        ) {
            Text("+")
        }
    }
}
Copy the code
  • Column

The Column will show each child below the previous child, similar to a LinearLayout with vertical orientation.

  1. Take a look at the following example
fun ColumnDemo() {
    Column {
        Text(text = "Hello World")
        Text(text = "Hello World2")
    }
}
Copy the code
  • Row

The Row shows each child next to the previous one, similar to a LinearLayout with horizontal orientation.

  1. Take a look at the following example
@Composable
@Preview
fun RowDemo() {

    Row {
       Text(text = "Hello World")
       Text(text = "Hello World2") 
    }
}
Copy the code
  • ConstraintLayout

ConstraintLayout in Compose is similar to ConstraintLayout in the classic Android View System

  1. Take a look at the following example
@Composable
fun ConstraintLayoutDemo() {
    ConstraintSet {
        val text1 = createRefFor("text1")
        val text2 = createRefFor("text2")
        val text3 = createRefFor("text3")

        constrain(text1) {
            start.linkTo(text2.end)
        }
        constrain(text2) {
            top.linkTo(text1.bottom)
        }

        constrain(text3) {
            start.linkTo(text2.end)
            top.linkTo(text2.bottom)
        }

    }
    ConstraintLayout {
        Text("Text1", Modifier.layoutId("text1"))
        Text("Text2", Modifier.layoutId("text2"))
        Text("This is a very long text", Modifier.layoutId("text3"))
    }
}
Copy the code

Meterial

  • TextFiled

TextField can be used to insert text. This is the equivalent of EditText in the Android View system.

  1. Take a quick look at the following example
@composable @preview fun TextFieldDemo() {Column(Modifier.padding(16.dp)) {val textState = remember { mutableStateOf(TextFieldValue()) } TextField( value = textState.value, onValueChange = { textState.value = it } ) Text("The textfield has this text: " + textState.value.text) } }Copy the code
  • Button

A Button has an onClick function that allows you to add text-composable or any other composables as a child of a Button.

  1. Take a quick look at the following example, which simply adds the onclick, colors parameter
@Composable
fun ButtonExample() {
    Button(
        onClick = { /*TODO*/ },
        colors = ButtonDefaults.textButtonColors(backgroundColor = Color.Red)
    ) {
        Text("Button")
    }

}
Copy the code
  • Switch

Used as a switch switch in compose, here’s a simple example

@composable @Preview Fun SwitchDemo() {val checkedState = Remember {mutableStateOf(true)} Switch(checked = checkedState.value, onCheckedChange = { checkedState.value = it }) }Copy the code
  • Card

Card is the equivalent of CardView in Compose and is used as the Card layout

  1. So let’s just use Card
@Composable @Preview fun CardDemo() { Card( Modifier .fillMaxSize() .padding(8.dp),elevation = 8.dp) { Text(text = "This  is a Card") } }Copy the code
  • AlertDialog

Implement the Compose built-in dialog, which is basically the same as the AlertDialog warning dialog in Android View

  1. Example: Set a button to display a dialog box when onClick is true
fun AlertDialogSample() {
    MaterialTheme {
        Column {
            val openDialog = remember { mutableStateOf(false) }

            Button(onClick = {
                openDialog.value = true
            }) {
                Text("Click me")
            }

            if (openDialog.value) {

                AlertDialog(
                    onDismissRequest = {
                        openDialog.value = false
                    },
                    title = {
                        Text(text = "Dialog Title")
                    },
                    text = {
                        Text("Here is a text ")
                    },
                    confirmButton = {
                        Button(
                            onClick = {
                                openDialog.value = false
                            }) {
                            Text("This is the Confirm Button")
                        }
                    },
                    dismissButton = {
                        Button(
                            onClick = {
                                openDialog.value = false
                            }) {
                            Text("This is the dismiss Button")
                        }
                    }
                )
            }
        }

    }
}
Copy the code
  • CircularProgressIndicator

As the name implies, CircularProgressIndicator can be used to display circular progress, which can be roughly divided into the following two

  1. If you don’t set the parameters, it just keeps going
CircularProgressIndicator()
Copy the code
  1. After setting the parameter, when you set a value for the Progress parameter, the indicator is displayed with the progress. For example, a progress of 0.5f fills it in half.
CircularProgressIndicator (progress = 0.5 f)Copy the code
  • Let’s look at an example to learn
@ Composable @ Preview fun CircularProgressIndicatorSample () {var progress by remember {mutableStateOf (0.1 f)} val animatedProgress = animateFloatAsState( targetValue = progress, animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec ).value Column(horizontalAlignment = Alignment.CenterHorizontally) { Spacer(Modifier.height(30.dp)) Text("CircularProgressIndicator with undefined progress") / / no arguments CircularProgressIndicator () Spacer (Modifier. The height (30. Dp)) Text (" CircularProgressIndicator with progress set by Buttons ") / / take parameters CircularProgressIndicator (progress = animatedProgress) Spacer (Modifier. The height (30. Dp)) OutlinedButton ( OnClick = {if (progress < 1f) progress += 0.1f}) {Text("Increase")} OutlinedButton(onClick = {if (progress > 0f)) Progress -= 0.1f}) {Text("Decrease")}}}Copy the code
  • LinearProgressIndicator

The LinearProgressIndicator can be used to display progress along a straight line, also known as a progress bar. Can be divided into two types:

  1. It doesn’t depend, but if you use the LinearProgressIndicator without the progress parameter, it will run forever.
LinearProgressIndicator()
Copy the code
  1. Yes, when you set a value for the Progress parameter, the indicator is displayed with the progress. For example, a progress of 0.5f fills it in half.
LinearProgressIndicator (progress = 0.5 f)Copy the code
  • Let’s take a look at some examples
@ Composable fun LinearProgressIndicatorSample () {var progress by remember {mutableStateOf (0.1 f)} val animatedProgress  = animateFloatAsState( targetValue = progress, animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec ).value Column(horizontalAlignment = Alignment.CenterHorizontally) { Spacer(Modifier.height(30.dp)) Text("LinearProgressIndicator with undefined progress") LinearProgressIndicator() Spacer(Modifier.height(30.dp)) Text("LinearProgressIndicator with progress set by buttons") LinearProgressIndicator(progress = animatedProgress) Spacer(Modifier.height(30.dp)) OutlinedButton( onClick = { if (progress < 1f) progress += 0.1f}) {Text("Increase")} OutlinedButton(onClick = {if (progress > 0f) progress -= 0.1f}) {Decrease ("Decrease")}}Copy the code
  • DropdownMenu

DropdownMenu Composable creates a DropdownMenu (DropdownMenu)

fun DropdownMenu(
    expanded: Boolean,
    onDismissRequest: () -> Unit,
    modifier: Modifier = Modifier,
    offset: DpOffset = DpOffset(0.dp, 0.dp),
    properties: PopupProperties = PopupProperties(focusable = true),
    content: @Composable ColumnScope.() -> Unit
)
Copy the code
  • Currently if true, a pop-up menu with dropdownContent will be displayed
  • CheckBox

As a selection box, you can choose whether to select the option operation

  1. Take a quick look at the following example, passing in the current state to determine whether it is selected
@Composable
fun CheckBoxDemo() {
    val checkedState = remember { mutableStateOf(true) }
    Checkbox(
        checked = checkedState.value,
        onCheckedChange = { checkedState.value = it }
    )
}
Copy the code
  • FloatingActionButton

Suspend button, and the Android View is divided into FloatActionButton and ExtendedFloatingActionButton (extensible)

  1. FloatActionButton
fun FloatingActionButtonDemo() {
    FloatingActionButton(onClick = { /*do something*/}) {
        Text("FloatingActionButton")
    }
}
Copy the code
  1. ExtendedFloatingActionButton
fun FloatingActionButtonDemo() {
    FloatingActionButton(onClick = { /*do something*/}) {
        Text("FloatingActionButton")
    }
}
Copy the code
  • ModalDrawerLayout

Use ModalDrawerLayout to create drawers

  1. Take a look at the example below and click the button to open the drawer
@Composable
@Preview
fun ModalDrawerSample() {
    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()


    ModalDrawer(
        drawerState = drawerState,
        drawerContent = {
            Column {
                Text("Text in Drawer")
                Button(onClick = {
                    scope.launch {
                        drawerState.close()
                    }
                }) {
                    Text("Close Drawer")
                }
            }
        }, content = {
            Column {
                Text(text = "Text in BodyContext")
                Button(onClick = {
                    scope.launch { 
                        drawerState.open()
                    }
                }) {
                    Text(text = "Click to open")
                }
            }
        })
}

Copy the code
  • RadioButton

In simple terms, the API enables radio buttons to perform radio operations

  1. Look at the official example
fun RadioButtonSample() {
    val radioOptions = listOf("A", "B", "C")
    val (selectedOption, onOptionSelected) = remember { mutableStateOf(radioOptions[1]) }
    Column {
        radioOptions.forEach { text ->
            Row(
                Modifier
                    .fillMaxWidth()
                    .selectable(
                        selected = (text == selectedOption),
                        onClick = {
                            onOptionSelected
                            (text)
                        }
                    )
                    .padding(horizontal = 16.dp)
            ) {
                RadioButton(
                    selected = (text == selectedOption),
                    onClick = { onOptionSelected(text) }
                )
                Text(
                    text = text,
                    style = MaterialTheme.typography.body1.merge(),
                    modifier = Modifier.padding(start = 16.dp)
                )
            }
        }
    }
}
Copy the code
  • Sacffold

Scaffolding is a structure layout that implements the basic material design layout. You can add things like TopBar, BottomBar, FAB, or Drawer.

  1. Take a look at the example below
@Composable
@Preview
fun ScaffoldDemo() {
    val materialBlue700 = Color(0xFF1976D2)
    val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Open))
    Scaffold(
        scaffoldState = scaffoldState,
        topBar = {
            //topBar
            TopAppBar(
                title = { Text("TopAppBar") },
                backgroundColor = materialBlue700
            )
        },
        //悬浮按钮位置
        floatingActionButtonPosition = FabPosition.End,
        //悬浮按钮
        floatingActionButton = {
            FloatingActionButton(onClick = {}) {
                Text("X")
            }
        },
        //抽屉内容
        drawerContent = { Text(text = "drawerContent") },
       //内容
        content = { Text("BodyContent") },
        //bottomBar
        bottomBar = { BottomAppBar(backgroundColor = materialBlue700) { Text("BottomAppBar") } }
    )
}
Copy the code
  • ScaffoldState: Using scaffoldState, you can set the open state of a drawer (drawerstate-opened or Drawerstate-closed).
  • FloatActionButton: You can add a FloatingActionButton here, and you can set any Composable, but you already have a FloatingActionButton set for this use case
  • FloatActionButtonPosition: after adding FAB, you can use this to specify its location. The default location is at the end of the layout.
  • DrawerContent: This is where you can set the contents of the drawer.
  • Content: Here is the main content of the stent
  • Bottombar: You can set a portion of the layout at the bottom of the screen. You can set any Composable, but the BottomAppBar is already created for this use case.

Animation

  • CrossFade

Fade in and out can be used to switch between composable objects using the fade in and out animation.

  1. Let’s take a look at the CrossFade example, where each time a button is clicked, the content of the screen changes with the duration of the 3 second animation.
@Composable @Preview fun CrossFadeDemo() { var currentColor by remember { mutableStateOf(MyColor.Red) } Column { Row { MyColor.values().forEach { myColors -> Button( onClick = { currentColor = myColors }, Modifier .weight(1f, true) .height(48.dp) .background(myColors.color), colors = ButtonDefaults.buttonColors(backgroundColor = myColors.color) ) { Text(text = myColors.name) } } } Crossfade(targetState = currentColor) { Crossfade(targetState = currentColor, animationSpec = tween(3000)) { selectedColor -> Box(modifier = Modifier.fillMaxSize().background(selectedColor.color)) }  } } } enum class MyColor(val color: Color) { Red(Color.Red), Green(Color.Green), Blue(Color.Blue) }Copy the code
  1. Think, how does this fade in and out work?
  • This example contains a screen with three buttons at the top. After clicking the button, the screen below changes to the color of your choice.
Crossfade(targetState = currentColor, animationSpec = tween(3000)) { selectedColor ->
   Box(modifier = Modifier.fillMaxSize().background(selectedColor.color))
}
Copy the code
  • Crossfade wants a state that can detect when to regroup. In this case, this is currentColor, which is the state with the selected color enumeration. This state is set to the current parameter.

  • Using the animationSpec parameter, you can set which animation should be used to switch between composable objects. The default parameter here is tween animation. You can choose between any class that implements the AnimationSpec. 3000 in this example is the duration that the tween animation will have.

  • The last parameter is the body of the Crossfade Composable. At this point, you must create the UI to be displayed. SelectedColor is the current value of currentColor. In this example, I use Box to display the color. Each time one of the three buttons is clicked, the value of currentColor changes, and Crossfade is refactored using an animation between the old and new UI.

The demo address

  • –> Everyone stamp here, can clone the document project demo
  • Since there are many APIS above, students can try the corresponding effect by themselves. You can use Preview to see the Preview effect

conclusion

  • Simply put, Jetpack Compose is the new next generation UI toolkit. It uses a declarative component-based paradigm to easily and quickly build uIs, written entirely in Kotlin and incorporating the Kotlin language style and ergonomics.
  • Jetpack Compose, written by example, is a new declarative UI toolkit designed to meet the needs of creating modern user interfaces. Start learning about Compose and the new API and Material components that make up the toolkit by examining the specific UI we created with it. In this article, I demonstrate how to customize and combine components to build real UIs, simplifying my development experience and opening up new possibilities through themes, animations, layouts, and other examples.
  • Jetpack Compose is now officially in beta, with no major changes to the API, and it’s time for developers to learn how to communicate, and maybe start adding it to their existing real-world applications
  • These few days of learning about Compose also made me think about how the tight integration with the tools can make the development experience better. Let’s continue learning about Compose and how it can help you build better applications!