Jetpack Compose is a modern Android UI toolkit written entirely in Kotlin, which uses all the features of the Kotlin language to help you build high quality Android applications easily and quickly. If you still don’t know what Jetpack Compose is? I suggest you read my previous 2 articles:

Jetpack Compose for Android

Jetpack Compose is more than just a UI framework!

Google announced Jetpack Compose at Last year’s Google IO conference, but in November of last year, it released its first preview version, Developer Preview1. Since then, it has been releasing a small version every two weeks. In the middle of the release of more than ten small versions, today, finally ushered in a major update, Developer Preview2 released.

One of the biggest concerns for Jetpack Compose Developer Preview1 is the lack of RecyclerView, Constriantlayout, animation, and more for the Compose version. These problems are solved in Preview2.

Of course, from Preview1 to the release of Preview2, there has been a lot of change, even a lot of APIS have changed, some properties or classes have been added or removed. The specific transformation is too much, I will not explain one by one here, interested in the official update log for each small version. Today we’ll take a look at some of the new features that PreView2 has added.

  • 1, the Modifier
  • 2, RecyclerView
  • 3, Constriantlayout
  • 4, animation,
  • 5, native View into Compose

Here we go!

1, the Modifier

First, the Modifier. In The Preview1 version, there is already the Modifier, but it is not used much, and its positioning is vague and confusing, because what the Modifier does can also be done by combining functions. One thing we noticed, for example, was that adding padding to the Compose function created a lot of nesting, because you had to nest a container to set the padding, so we moved a lot of the Modifier, which is now widely used, You can modify an element, a layout, or some other behavior. How to use the Modifier? Let’s start with an example:

First, we write a Compose function (the Compose component) and show a picture

@Composable
fun Greeting(a) {
    val (shape,setShape) = state<Shape> { CircleShape }
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop )
}
Copy the code

The image shows the original size, and then specify a size for the image, such as 256DP, to use the Modifier.

@Composable
fun Greeting(a) {
    val (shape,setShape) = state<Shape> { CircleShape }
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp))
}
Copy the code

After modification, both width and height are 256DP.

The modifier has a number of optional parameters, such as adding a padding or clipping the image to a circle.

@Composable
fun Greeting(a) {
    val (shape,setShape) = state<Shape> { CircleShape }
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
        )
}
Copy the code

The effect is this:

You can also add a border to the round avatar as follows:

@Composable
fun Greeting(a) {
    val (shape,setShape) = state<Shape> { CircleShape }
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
         .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape)
        )
}
Copy the code

The effect is as follows:

You can also add multiple borders at the same time, for example, I add 2 more:

@Composable
fun Greeting(a) {
    val (shape,setShape) = state<Shape> { CircleShape }
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
         .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape)
         .drawBorder(12.dp,MaterialTheme.colors.secondary,shape = shape)
         .drawBorder(18.dp,MaterialTheme.colors.background,shape = shape)
        )
}
Copy the code

The effect would look like this:

Setting the click event is also in the modifier, for example, we need to change the shape of the View after clicking the image. The previous View is troublesome, but Jetpack Compose is very simple, add the following code in the modifier:

@Composable
fun Greeting() {
    val (shape,setShape) = state<Shape> { CircleShape } Image(asset = imageResource(R.drawable.androidstudio), contentScale = ContentScale.Crop, modifier = Modifier.size(256.dp) .padding(16.dp) .drawShadow(8.dp,shape = shape) .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape) .drawBorder(12.dp,MaterialTheme.colors.secondary,shape = Shape). DrawBorder (18. The dp, MaterialTheme. Colors. Background, shape = shape). The clickable {/ / click eventsetShape(
                 if(shape == CircleShape)
                     CutCornerShape(topLeft = 32.dp,bottomRight = 32.dp)
                else
                     CircleShape
             )
         }
        )
}
Copy the code

In the code above, we also added judgment, if the current shape is CircleShape, we change the shape, otherwise we set it to CircleShape, and the effect is to click on the image, and the shape toggle back and forth between the two effects.

The effect is as follows:

RecyclerView in Jetpack Compose

RecyclerView is a common component used to display big data lists in our Android development. It can help us recycle and reuse views and have a good performance experience. When Jetpack Developer PreView1 first came out, I looked for this component on the official website or in the code base. It is a pity that I have searched all the materials and found none. Finally, I only found a component called VerticalScroller. It can be used to display lists, but it is not RecyclerView, it is similar to our ScrollView, that is to say, it is ok to display lists with a small amount of data, because it has no reuse mechanism, display large lists, memory is worried, will OOM.

But in this Preview2, RecyclerView was finally looked forward to, component name is called: AdapterList, it corresponds to our native Android development RecyclerView. In the past, it was very complicated to write a list. We had to write XML,Adapter,ViewHolder, etc. Finally, we had to initialize and bind data in the Activity/Fragment. Using the list in Jetpack Compose is shockingly simple. Let’s see how we present a list:

@Composable
fun generateList(context: Context) {
    val list = mutableListOf<String>()
    // Prepare data
    for (i in 1.100.) {
        list.add(i.toString())
    }
    AdapterList(data = list) {
        Card(
            shape = MaterialTheme.shapes.medium,
            modifier = Modifier
                .preferredSize(context.resources.displayMetrics.widthPixels.dp, 60.dp)
                .padding(10.dp)
        ) {

            Box(gravity = ContentGravity.Center) {
                ProvideEmphasis(EmphasisAmbient.current.medium) {
                    Text(
                        text = it,
                        style = MaterialTheme.typography.body2
                    )
                }
            }

        }
        Spacer(modifier = Modifier.preferredHeight(10.dp))
    }
}
Copy the code

See, with just a few lines of code, our list is complete. Explain the code: The initial data preparation is nothing, adding 100 data to the list, and passing the data source to the AdapterList. Each Item in the list is a Card, using the Card component, which displays a Text Text. Finally, Spacer sets the spacing between items. ItemDecoration, look at the effect:

3. Constriantlayout

Constriantlayout is a very powerful layout and one of the most popular layouts in Android development right now. When Jetpack Compose Preview1 came out, many developers had a question: How do you use Constriantlayout in Compose? How it will work is a really interesting question. Because in Jetpack Compose, all the components are composed functions and cannot get views, it is a real challenge to constrain the relationship between them. Now that that’s out of the way, let’s take a look at the use of Compose’s Constriantlayout with a few small examples.

ViewB is to the right of ViewA. The top of ViewB is aligned with the bottom of ViewA.

Code:

@Composable
fun GreetConstraintLayout(context: Context) {
    ConstraintLayout(constraintSet = ConstraintSet {
        val viewA = tag("ViewTagA").apply {
            left constrainTo parent.left
            centerVertically()
        }
       val viewB =  tag("ViewTagB").apply {
            left constrainTo viewA.right
            centerVertically()
            top constrainTo viewA.bottom
        }
    }, modifier = Modifier.preferredSize(context.screenWidth().dp,400.dp).drawBackground(Color.LightGray)) {

        Box(
            modifier = Modifier.tag("ViewTagA").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Blue,
            gravity = ContentGravity.Center
        ) {
            Text(text = "A")
        }
        Box(
            modifier = Modifier.tag("ViewTagB").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Green,
            gravity = ContentGravity.Center
        ) {
            Text(text = "B")}}}Copy the code

Explain the above code: To define constraints in ConstraintSet, use the Tag to create a constraint, later we can use the constraints we defined by this Tag, return is a ConstrainedLayoutReference, ViewA on the left and the left side of the parent component alignment, vertical center. The left of ViewB is aligned with the right of ViewA, and top is aligned with the bottom of ViewA. It’s also vertically centered.

So ViewB, for example, uses ViewA as a constraint.

When used later, apply the constraint directly to the component with Modifier. Tag ().

If this isn’t the best, there’s also a powerful way to add logic to layout constraints, such as: I have a ViewC and its position can be one of two things:

  • 1. Align the left side of ViewC with the right side of ViewA
  • 2. Align the left of View C with the right of ViewB

How do I code it? Start with a Boolean variable called hasFlag(whatever its name is, its value will be true in some cases, false in others depending on your business logic)

 val hasFlag = true // Its value is true in some cases and false in others, depending on your business logic
 
 tag("ViewC").apply {
            // As the judgment condition changes, the constraint also changes
            left constrainTo (if(hasFlag) viewA else viewB).right
            bottom constrainTo viewB.top
        }

Copy the code

The complete code is as follows:

@Composable
fun GreetConstraintLayout(context: Context) {
    ConstraintLayout(constraintSet = ConstraintSet {
        val hasFlag = true // Its value is true in some cases and false in others, depending on your business logic
        val viewA = tag("ViewTagA").apply {
            left constrainTo parent.left
            centerVertically()
        }
       val viewB =  tag("ViewTagB").apply {
            left constrainTo viewA.right
            centerVertically()
            top constrainTo viewA.bottom
        }
        tag("ViewC").apply {
            // As the judgment condition changes, the constraint also changes
            left constrainTo (if(hasFlag) viewA else viewB).right
            bottom constrainTo viewB.top
        }
    }, modifier = Modifier.preferredSize(context.screenWidth().dp,400.dp).drawBackground(Color.LightGray)) {

        Box(
            modifier = Modifier.tag("ViewTagA").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Blue,
            gravity = ContentGravity.Center
        ) {
            Text(text = "A")
        }
        Box(
            modifier = Modifier.tag("ViewTagB").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Green,
            gravity = ContentGravity.Center
        ) {
            Text(text = "B")
        }
        Box(
            modifier = Modifier.tag("ViewC").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Red,
            gravity = ContentGravity.Center
        ) {
            Text(text = "C")}}}Copy the code

HasFlag =true:

HasFlag =false:

Some of the other ConstraintLayout attributes are the same as ConstraintLayout we use now, so you can try them out if you are interested.

4. The animation

Animation support for Jetpack Compose is one of the main concerns for developers. In this section, we will take a look at the application for animation in Compose.

As shown above, for a simple property animation, the picture has selected/unselected states, with unselected -> selected, and a square -> circle animation, accompanied by alpha animation.

The code is as follows:

@Composable
fun GreetAnimate(a){
    //
    val (selected,onValueChange) = state { false }
    / / the radius changes
    val radius = animate(if(selected) 100.dp else 8.dp)
    / / alpha animation
    val selectAlpha = animate(if(selected) 0.4f else 1.0f)

   Surface(shape = RoundedCornerShape(
       topLeft = radius,
       topRight = radius,
       bottomRight = radius,
       bottomLeft = radius
   )) {
       Toggleable(
           value = selected,
           onValueChange = onValueChange,
           modifier = Modifier.ripple()
       ) {

           Image(
               asset = imageResource(R.drawable.androidstudio),
               modifier = Modifier.preferredSize(200.dp,200.dp),
               contentScale = ContentScale.Crop,
               alpha = selectAlpha
           )
       }
   }
}
Copy the code

Animate Compose is composed using the animate Compose function. Just give it different target values and the animate Compose function will do the same for you once the animation is created.

Note that animate created animations cannot be cancelled. To create an animation that can be cancelled, use animatedValue. There are other two similar animation function: animatedFloat, animatedColor

What? You said it looked familiar? That is not well, ObjectAnimator, ValueAnimator, you fine goods, more about the use of animation here is not opened, students interested in down try myself.

4. Compatibility with native View

A new language, a new framework, it’s necessary to think about compatibility, just like Kotlin, we don’t have to rewrite the whole project all at once with Kotlin, you can add a new class, a new module that uses Kotlin, because they’re completely interoperable with Java.

For example, Jetpack Compose takes a page out of your experience, and we’re going to use Jetpack Compose. You can also take your time, leave the previous code in place, and add it to your new module bit by bit. This involves compatibility with the original View. You can use AndroidView to be compatible with previous Views.

For example, what if I want to use a Webview in my Jetpack Compose and it doesn’t provide one? Don’t worry, just use the old one.

First, create an XML file called webView.xml and add the webView layout:

<?xml version="1.0" encoding="utf-8"? >
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</WebView>
Copy the code

Then write a compose function and use AndroidView to load it:

@Composable
fun androidView(a){
    AndroidView(R.layout.webview){ view ->
        val webView = view as WebView
        webView.settings.javaScriptEnabled =true
        webView.settings.allowFileAccess = true
        webView.settings.allowContentAccess = true
        webView.loadUrl("https://juejin.cn")}}Copy the code

Load a native Webview, then load the url of the nuggets in the Webview, the effect is as follows:

Look at the AndroidView function signature:

@Composable
// TODO(popam): support modifiers here
fun AndroidView(@LayoutRes resId: Int, postInflationCallback: (View) -> Unit = { _ -> }) {
    AndroidViewHolder(
        postInflationCallback = postInflationCallback,
        resId = resId
    )
}
Copy the code

You receive a layout file resource ID and a postInflationCallback that will be called when the View is inflate out, and you can use it in the callback.

Note, however, that callbacks are usually called on the main thread.

5. To summarize

In general, the Developer PreView2 update is more, and many of the API changed, added some key components such as AdapterList ConstraintLayout, animation components, such as use way and also there are many different Preview1. Take a look at Google’s timeline for Jetpack Compose:

  • 2019.5 Announce Jetpack Compose
  • 2019.11 First Developer Preview
  • 2020.6 Second Developer Preview
  • Alpha will be released in summer 2020
  • 2021 will be release 1.0

However, many of the APIS are still not in final form, and as you can see, they change a lot with each release and are still not available for commercial projects. But as for Jetpack Compose itself, I am looking forward to it. As can be seen from the schedule above, the first release will be released next year. Please stay tuned.

For example, the latest version of Jetpack Compose requires Android Studio 4.2 or higher to use, but for those who want to check out, use Android Studio 4.2 Canary. Go to the official website to download!

Minor version log list please see: developer.android.com/jetpack/and…

YouTube video introduction: www.youtube.com/watch?v=U5B…

The article was first published on the public account: “Technology TOP”, there are dry articles updated every day, you can search wechat “technology TOP” first time to read, reply [mind map] [interview] [resume] yes, I prepare some Android advanced route, interview guidance and resume template for you