19

In Jetpack Compose, how can I display a large list of data while laying out only the visible items, instead of composing and laying out every item on the initial layout pass? This would be similar to RecyclerView and ListView in the View toolkit.

One could use a for loop to place all of the components inside of a Column in a VerticalScroller, but this would result in dropped frames and poor performance on larger numbers of items.

Community
  • 1
  • 1
Ryan M
  • 18,333
  • 31
  • 67
  • 74

3 Answers3

38

The equivalent component to RecyclerView or ListView in Jetpack Compose is LazyColumn for a vertical list and LazyRow for a horizontal list. These compose and lay out only the currently visible items.

You can use it by formatting your data as a list and passing it with a @Composable callback that emits the UI for a given item in the list. For example:

val myData = listOf("Hello,", "world!")
LazyColumn {
    items(myData) { item ->
        Text(text = item)
    }
}
val myData = listOf("Hello,", "world!")
LazyRow { 
    items(myData) { item ->
        Text(text = item)
    }
}

You can also specify individual items one at a time:

LazyColumn {
    item {
        Text("Hello,")
    }
    item {
        Text("world!")
    }
}
LazyRow { 
    item {
        Text("Hello,")
    }
    item {
        Text("world!")
    }
}

There are also indexed variants, which provide the index in the collection in addition to the item itself:

val myData = listOf("Hello,", "world!")
LazyColumn {
    itemsIndexed(myData) { index, item ->
        Text(text = "Item #$index is $item")
    }
}
val myData = listOf("Hello,", "world!")
LazyRow { 
    itemsIndexed(myData) { index, item ->
        Text(text = "Item #$index is $item")
    }
}

These APIs were, in previous releases, known as AdapterList, LazyColumnItems/LazyRowItems, and LazyColumnFor/LazyRowFor.

Ryan M
  • 18,333
  • 31
  • 67
  • 74
  • 1
    Thank you. I was wondering are there also any performance tips for using ```LazyColumnFor``` or ```LazyRowFor```? The information about this is really scarce right now on the web. It's really slow even in release mode, I tested with ```ConstraintLayout``` composable, which has a few images, some text and three buttons. – Rosen Dimov Aug 16 '20 at 10:17
  • 1
    For more complex items, in RecyclerViews there is a differentiation between item-ID and item-Content, to identify content changes vs new items. Do you know how to achieve this differentiation in `LazyColumnFor`? – Sebas LG Aug 30 '20 at 07:49
  • 3
    @SebasLG there is not currently such a differentiation. To a large degree, it's unnecessary: if you use Compose state variables to hold the changing content, the child will recompose without being treated as a new item. – Ryan M Aug 30 '20 at 10:38
  • 1
    @RosenDimov have you had news? I am desperately trying to use LazyColumn but the perf is really really poor at the moment. – sachadso Dec 22 '20 at 13:37
  • 1
    @sachadso, it seems that it's still too early and I guess more optimizations are to come. I last tried with ```alpha06```, and there was a slight boost, related to this issue that I logged (and a few other issues): https://issuetracker.google.com/issues/165028371 But still far from ```RecyclerView```'s behaviour. – Rosen Dimov Dec 23 '20 at 10:10
  • what if we want a custom item adapter with listview? – Ramanjeet Aug 13 '21 at 10:14
  • checkout here https://www.youtube.com/watch?v=wH5pTjz3STI – Sachin Rajput Sep 12 '22 at 09:14
3

Update in dev.16

  • [ScrollableColumn] for vertically scrolling list
  • [ScrollableRow] for horizontally scrolling list

Check it's implementation from ListCardViewTemplate


You can get the same essence of RecyclerView or ListView in JetpackCompose using AdapterList that's renamed in dev.14 preview version.

  • [LazyColumnItems] for vertically scrolling list
  • [LazyRowItems] for horizontally scrolling list

Check what's documentation says:

It was also moved into a lazy sub-package and split into two files. Plus I renamed params:

  1. data -> items. this seems to be more meaningful then just raw data
  2. itemCallback -> itemContent. this is more meaningful and we generally don't use word callback in the lambda names, especially for composable lambdas

Check how to use it:

@Composable
fun <T> LazyColumnItems(
  items: List<T>,
  modifier: Modifier = Modifier,
  itemContent: @Composable (T) -> Unit
) {
    LazyItems(items, modifier, itemContent, isVertical = true)
}

In .KT

LazyColumnItems(items = (0..50).toList()) { item ->
    cardViewImplementer(item)
 }

From my perspective LazyColumnItem or LazyRowItem is not working properly if your item layout is complex because it's stuck the list as a comparison to VerticalScroller working fine in this scenario.

Ali Azaz Alam
  • 1,782
  • 1
  • 16
  • 27
  • ```LazyRowItems``` and ```LazyColumnItems``` are deprecated now in favor of ```LazyRowFor``` and ```LazyColumnFor``` as per docs: https://developer.android.com/reference/kotlin/androidx/compose/foundation/lazy/package-summary.html?hl=pt-brhttps%3A%2F%2Fdeveloper.android.com%2Fstudio%2Frun%2Fmanaging-avds.html%3Fhl%3Dpt-br%22https%3A%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22%22https%3A%2F%2Fsupport.google.com%2Fmerchants%2Fanswer%2F188494%5C%22#lazyrowitems – Rosen Dimov Aug 15 '20 at 12:08
  • yes in dev.16 they updated it. I've modified the answer – Ali Azaz Alam Aug 21 '20 at 21:49
  • check implementation here https://www.youtube.com/watch?v=wH5pTjz3STI – Sachin Rajput Sep 12 '22 at 09:14
0

LazyColumn and LazyRow are euquavalant to Recycleview.

/*
*
*  LazyColumn produces a vertically scrolling list,
*  LazyRow produces a horizontally scrolling list.
*
* */

@Composable
fun addUserList(userDataList: List<User>) {

    val context = LocalContext.current

    Column(
        modifier = Modifier
            .fillMaxSize()
            .fillMaxWidth()
            .fillMaxHeight()
    ) {

        LazyColumn(
            contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp),
            verticalArrangement = Arrangement.spacedBy(3.dp),
        ) {

            // header
            item {
                Text(text = "I'm header.")
            }

            // main content
            items(
                userDataList,
                itemContent = { user ->
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .clickable {
                                context.showToast("you clicked ${user.name}")
                            },
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(16.dp),


                        ) {
                        //Text(text = user.name)
                        UserView2(user = user)


                    }

                    // add diver color
                    Divider(color = Color.LightGray)

                },

                )


            // footer
            item {
                Text(
                    text = "I'm footer.",
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = TextAlign.Center
                )
            }
        }
    }
}

//===============================//

Make sure you added glide compose dependency.

implementation "com.github.bumptech.glide:compose:1.0.0-alpha.1"

Row View :

// This is implementation for row view or equavalant to view in adapter which inflate for each row/record.

@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun UserView2(user: User) {
    GlideImage(
        model = "https://datanapps.com/public/dnarestapi/user/renold1.png",
        contentDescription = "getString(R.id.picture_of_cat)",
        // shows an error ImageBitmap when the request failed.

        modifier = Modifier
            .background(color = Color.Cyan, shape = CircleShape)
            .padding(PaddingValues(1.dp))
            .clickable(enabled = true) { }
            .width(50.dp)
            .height(50.dp),
    )
    Text(text = user.name, color = Color.Blue)
    Text(text = user.email, color = Color.Cyan)
}

Read More :

Compose List >> https://developer.android.com/jetpack/compose/lists

Glide compose >> https://bumptech.github.io/glide/int/compose.html

Compose list Video >> https://www.youtube.com/watch?v=1ANt65eoNhQ&t=1s&ab_channel=AndroidDevelopers

Output :

enter image description here

starball
  • 20,030
  • 7
  • 43
  • 238
Yogendra
  • 4,817
  • 1
  • 28
  • 21