• Jetpack Compose: An Easy way to RecyclerView (Part I)
  • By Waseef Akhtar
  • Translation from: The Gold Project
  • This article is permalink: github.com/xitu/gold-m…
  • Translator: Kimho
  • Proofreader: PingHGao, lsvih

Jetpack Compose: RecyclerView for Simpler RecyclerView

If you’re new to the Jetpack Compose library and, like ME, have seen all the cool UI screens and animations on the Internet, you might be a little overwhelmed, but also curious about how things work in Compose.

Since I’m new to the Jetpack Compose library like most of you, the recent Android Jetpack Compose Development Challenge was a great learning opportunity for me, I can use the Jetpack Compose library to get started and write some UI. Because I learned so much from a basic app, and I think what I learned could be written into a great series of blog posts to help you all.

Through this series of blog posts, we will create a basic application that includes displaying a list of adopted puppies, designing the overall style of our application, and implementing a detailed view for each puppy.

At the end of this series, our application looks like this:

So don’t hesitate, let’s get started!

Background ✍ ️

  • The Jetpack Compose library is a newly released UI toolkit that will soon replace the current method of building UIs using XML files by building native UIs for Android applications using Kotlin.
  • It is written entirely in Kotlin.
  • It simplifies UI development on Android with less code and powerful tools.
  • For more information please click: developer.android.com/jetpack/com…

The premise ☝ ️

Because the Jetpack Compose library is not fully supported in the current stable version of Android Studio, I used the version of Android Studio 2020.3.1 Canary 14 for this tutorial. But I’m confident that the steps outlined in this article will work on newer and more stable versions of Android Studio, once they start supporting the Jetpack Compose library.

Project Settings ⚙️

To get started, do the following:

  1. Create a new project.
  2. Select an Empty Compose Activity project template and give your application a name. This will create an empty Android project.

Run the project 🏃♂️

Before we start writing the first line of Jetpack Compose code, let’s run the current project set up for us by AndroidStudio. Since we are using Jetpack Compose and an unstable/preview version of Android Studio, it is likely that we will encounter some unknown issues. Therefore, it is a good idea to rerunning the project after each code change.

In my case, after running the project for the first time, I encountered the following situation:

An exception occurred applying plugin request [id: 'com.android.application'] > Failed to apply plugin 'com.android.internal.application'. > com.android.builder.errors.EvalIssueException: The option 'Android.EnableBuildCache' is deprecated. The current default is 'false'. It was removed in version 7.0 the Android Gradle plugin. The Android-specific build caches were superseded by the Gradle build cache (https://docs.gradle.org/current/userguide/build_cache.html). * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.Copy the code

To solve this problem:

  1. Open thegradle.propertiesFile.
  2. deleteandroid.enableBuildCache=trueThis line.

When you run the project again, you should see the sample Compose application that Android Studio built for us.

After a successful run, we’re ready to get started!

Write our first line, the Compose code ✨

To start writing our application, we first need to build our application with what I call the Jetpack Compose protocol, because I see this protocol a lot in Google Codelabs.

First thing:

  1. Open the mainActivity. kt file.

  2. Create a new combinable function in your MainActivity class.

@Composable
fun MyApp(a) {
    Scaffold(
        content = {
            BarkHomeContent()
        }
    )
}
Copy the code
  1. ifScaffoldImport it into a file if it is not automatically imported and appears as an unresolved reference.

What is Scaffold? 🤔

If you read the Scaffold definition, you can see that Scaffold implements the basic Material Design visual layout structure in Compose. So in general, it’s a good idea to start your screen drawing with Android’s native visual layout structure.

  1. By calling it in the onCreate methodMyApp()Replace the Hello World greeting in the example.
override fun onCreate(savedInstanceState: Bundle?). {
    super.onCreate(savedInstanceState)
    setContent {
        BarkTheme {
            MyApp()
        }
    }
}
Copy the code

Next, we need to write the content parameter BarkHomeContent() that is added to the Scaffold.

But first, we knew we needed to show a list of puppies, with some details about each puppy, and maybe a picture. To do this, we need to create a Data class to hold information about each puppy, and a Data Provider class to provide us with a list of puppies that will be displayed in our list in the correct order.

Set puppies for adoption 🐶

In a real world scenario, our data is usually provided by the back end through some sort of RESTful API, and we need to process these apis asynchronously and write different streams for them. But for the purposes of learning, we’re going to fake the data, write down all our puppy information and add pictures of them to our app.

To do this:

  1. I’ll create a new one calledPuppy.ktIn the class.
  2. Write a data class that contains all the property fields to populate the list items with the following:
data class Puppy(
    val id: Int.val title: String,
    val description: String,
    val puppyImageId: Int = 0
)
Copy the code

Next, we’re going to add some cute puppy pictures for each puppy. To make the process easier, you can always download this set of photos from my GitHub project.

After downloading,

  1. Select all files.
  2. Copy these files.
  3. Under the /res directory in Android Studio, select the /drawable folder and paste all the files.

  1. When the prompt dialog box asks which directory to add them to, selectdrawable-nodpiIf you don’t have this folder, you can/resYou can manually create a folder in the/drawableFolder)

Now we’re finally going to write the DataProvider class to construct the data for the list.

  1. I’ll create a new one calledDataProvider.ktIn the class.
  2. Write an object declaration and create a list of information for each puppy (feel free to copy all the text to save time building the application)
object DataProvider {

    val puppyList = listOf(
        Puppy(
            id = 1,
            title = "Monty",
            description = "Monty enjoys chicken treats and cuddling while watching Seinfeld.",
            puppyImageId = R.drawable.p1
        ),
        Puppy(
            id = 2,
            title = "Jubilee",
            description = "Jubilee enjoys thoughtful discussions by the campfire.",
            puppyImageId = R.drawable.p2
        ),
        Puppy(
            id = 3,
            title = "Beezy",
            description = "Beezy's favorite past-time is helping you choose your brand color.",
            puppyImageId = R.drawable.p3
        ),
        Puppy(
            id = 4,
            title = "Mochi",
            description = "Mochi is the perfect \"rubbery ducky\" debugging pup, always listening.",
            puppyImageId = R.drawable.p4
        ),
        Puppy(
            id = 5,
            title = "Brewery",
            description = "Brewery loves fetching you your favorite homebrew.",
            puppyImageId = R.drawable.p5
        ),
        Puppy(
            id = 6,
            title = "Lucy",
            description = "Picture yourself in a boat on a river, Lucy is a pup with kaleidoscope eyes.",
            puppyImageId = R.drawable.p6
        ),
        Puppy(
            id = 7,
            title = "Astro",
            description = "Is it a bird? A plane? No, it's Astro blasting off into your heart!",
            puppyImageId = R.drawable.p7
        ),
        Puppy(
            id = 8,
            title = "Boo",
            description = "Boo is just a teddy bear in disguise. What he lacks in grace, he makes up in charm.",
            puppyImageId = R.drawable.p8
        ),
        Puppy(
            id = 9,
            title = "Pippa",
            description = "Pippa likes to look out the window and write pup-poetry.",
            puppyImageId = R.drawable.p9
        ),
        Puppy(
            id = 10,
            title = "Coco",
            description = "Coco enjoys getting pampered at the local puppy spa.",
            puppyImageId = R.drawable.p10
        ),
        Puppy(
            id = 11,
            title = "Brody",
            description = "Brody is a good boy, waiting for your next command.",
            puppyImageId = R.drawable.p11
        ),
        Puppy(
            id = 12,
            title = "Stella",
            description = "Stella! Calm and always up for a challenge, she's the perfect companion.",
            puppyImageId = R.drawable.p12
        ),
    )
}
Copy the code

We’re ready to adopt our puppy. 🐶

Show puppies in the list 📝

Now, back to where we left off when we called BarkHomeContent() in MyApp(), we’ll finally create a list item and populate the list with the data we just created.

First things first,

  1. Create a new class named barkhome.kt.

  2. Add a combinable function called BarkHomeContent() to the new class.

@Composable
fun BarkHomeContent(a) {
    val puppies = remember { DataProvider.puppyList }
    LazyColumn(
        contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
    ) {
        items(
            items = puppies,
            itemContent = {
                PuppyListItem(puppy = it)
            })
    }
}
Copy the code
  1. Import all missing references.

Note: You may notice that different versions of the items function may be required at this point, given that the items = argument is not parsed. In this case, you need to manually import it at the top of the class reference: import androidx.com pose. Foundation. Lazy. The items

Now, there’s a lot going on here, and let’s explain each of them.

  1. In line 3, we define a puppies variable, but with an remember {} keyword. When the state of the list changes, the remember function in the combinable function stores only the current state of the variable (in this case, an exciting variable). This is useful in a real world scenario if we have any UI elements that allow the user to change the state of the list. In this scenario, the list changes from the back end or from user events. In our current situation, we don’t have such a feature, but it’s still a good practice to be able to maintain the status of our puppy list. For more information, see the documentation.

  2. In line 4, we call the Composable of the LazyColumn. This is the equivalent of RecyclerView, which we as Android developers are so familiar with. This really needs to be a big celebration because of how easy it is to create a dynamic list with Jetpack Compose. 🎉

  3. In line 5, in the LazyColumn parameter, we give it a nice little fill block to give each of our list items a little breathing space.

  4. In the contents of LazyColumn, lines 7-11, we call the Items function, which takes the puppies list as its first argument and calls a defendable puppies (which we’ll create in the next step), which populates the list item combination into each item in the list.

Create the list item 📝

Next, we will create a composable list item, which we will call PuppyListItem:

  1. I’ll create a new one calledPuppyListItem.ktThe Kotlin file.
  2. In order toPuppyWrite a new simple composable function in a class that takes a type as an argument.
  3. Inside the function, create aRowIs used to represent a row in a list.
  4. inRowCreate a column with two pieces of text and write the dog’s name in the first box and in the secondview detail.
@Composable
fun PuppyListItem(puppy: Puppy) {
    Row {
        Column {
            Text(text = puppy.title, style = typography.h6)
            Text(text = "VIEW DETAIL", style = typography.caption)
        }
    }
}
Copy the code

This is what happens when you run the application after you create PuppyListItem.

Not very nice. But we can design our list items in a few simple steps.

Design the list item 🎨

  1. Add a little padding and maximize the width of the text while leaving some breathing space.
Row {
    Column(
        modifier = Modifier
            .padding(16.dp)
            .fillMaxWidth()
            .align(Alignment.CenterVertically)
    ) {
        Text(text = puppy.title, style = typography.h6)
        Text(text = "VIEW DETAIL", style = typography.caption)
    }
}
Copy the code

  1. With acardComponents put yourRowCircle it. You can make it whatever you want.
Card(
    modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp).fillMaxWidth(),
    elevation = 2.dp,
    backgroundColor = Color.White,
    shape = RoundedCornerShape(corner = CornerSize(16.dp))
) {
    Row {
        Column(
            modifier = Modifier
                .padding(16.dp)
                .fillMaxWidth()
                .align(Alignment.CenterVertically)
        ) {
            Text(text = puppy.title, style = typography.h6)
            Text(text = "VIEW DETAIL", style = typography.caption)
        }
    }
}
Copy the code

Finally, we need to add an image for each puppy. To do this:

  1. Create a new combinable function PuppyImage() under PuppyListItem(), passing the puppy parameter.

  2. Call the Image combinable function and set the style as needed:


@Composable
private fun PuppyImage(puppy: Puppy) {
    Image(
        painter = painterResource(id = puppy.puppyImageId),
        contentDescription = null,
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .padding(8.dp)
            .size(84.dp)
            .clip(RoundedCornerShape(corner = CornerSize(16.dp)))
    )
}
Copy the code
  1. Finally, in thePuppyListItem()In theRowIn the first callPuppyImage().
@Composable
fun PuppyListItem(puppy: Puppy) {
    Card(
        modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp).fillMaxWidth(),
        elevation = 2.dp,
        backgroundColor = Color.White,
        shape = RoundedCornerShape(corner = CornerSize(16.dp))

    ) {
        Row {
            PuppyImage(puppy)
            Column(
                modifier = Modifier
                    .padding(16.dp)
                    .fillMaxWidth()
                    .align(Alignment.CenterVertically)) {
                Text(text = puppy.title, style = typography.h6)
                Text(text = "VIEW DETAIL", style = typography.caption)

            }
        }
    }
}
Copy the code

Wow! We have populated the dynamic list view with data. So this article is over.

The two remaining things are:

  1. Design the application according to our final look.
  2. Implement the details page.

Happy coding! 💻

Source code for the final version of this article

If you find any errors in the translation or other areas that need improvement, you are welcome to revise and PR the translation in the Gold Translation program, and you can also get corresponding bonus points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


Diggings translation project is a community for translating quality Internet technical articles from diggings English sharing articles. The content covers the fields of Android, iOS, front end, back end, blockchain, products, design, artificial intelligence and so on. For more high-quality translations, please keep paying attention to The Translation Project, official weibo and zhihu column.