Obaida Al Mostarihi
Obaida Al Mostarihi

Reputation: 499

Jetpack compoes lazycolumn skipping frames (lagging)

Hi i'm trying to implement a lazycolumn of a list of posts, I tested it on the emulator api 21 and 29 and it looks kinda smooth on the api 29 it's a little bit laggy, when I tested it on a physical device it was lagging, It looks like it's skipping frames or something..

I tried to remove some views that uses imageVector to see if that was the problem and still the same problem.

This is my composable view:

@Composable
fun HomePostView(
    category: String,
    imagesUrl: List<String> = listOf(imageHolder),
    doctorProfileImage: String = imageUrl,
    title: String,
    subTitle: String
) {


    Card(
        shape = PostCardShape.large, modifier = Modifier
            .padding(horizontal = 3.dp)
            .fillMaxWidth()
    ) {

        Column {

            PostTopView(
                category = category,
                onOptionsClicked = { /*TODO option click*/ },
                onBookmarkClicked = {/*TODO bookmark click*/ })

            CoilImage(
                data = imagesUrl[0],
                fadeIn = true,
                contentDescription = "post_image",
                modifier = Modifier
                    .fillMaxWidth()
                    .requiredHeight(190.dp)
                    .padding(horizontal = contentPadding),
                contentScale = ContentScale.Crop
            )

            Spacer(modifier = Modifier.height(10.dp))

            PostDoctorContent(
                doctorProfileImage = doctorProfileImage,
                title = title,
                subTitle = subTitle
            )
            Spacer(modifier = Modifier.height(contentPadding))

            PostBottomView(likesCount = 293, commentsCount = 22)

            Spacer(modifier = Modifier.height(contentPadding))

        }


    }
    Spacer(modifier = Modifier.height(10.dp))


}


@Composable
private fun PostDoctorContent(doctorProfileImage: String, title: String, subTitle: String) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = contentPadding)
    ) {

        CoilImage(data = doctorProfileImage,
            contentScale = ContentScale.Crop,
            contentDescription = null,
            fadeIn = true,
            modifier = Modifier
                .size(30.dp)
                .clip(CircleShape)
                .clickable {
                    /*Todo on doctor profile clicked*/
                })


        Column {
            Text(
                text = title, fontSize = 14.sp, maxLines = 1,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier.padding(horizontal = contentPadding)
            )

            Text(
                text = subTitle,
                fontSize = 11.sp,
                color = LightTextColor,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier.padding(horizontal = contentPadding)
            )
        }
    }


}

@Composable
private fun PostBottomView(likesCount: Long, commentsCount: Long) {
    Row(
        modifier = Modifier.padding(horizontal = contentPadding),
        verticalAlignment = Alignment.CenterVertically
    ) {

        Row(
            Modifier
                .clip(RoundedCornerShape(50))
                .clickable { /*Todo on like clicked*/ }
                .padding(5.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Icon(
                imageVector = ImageVector.vectorResource(id = R.drawable.ic_heart),
                contentDescription = "Like"
            )
            Spacer(modifier = Modifier.width(5.dp))
            Text(text = likesCount.toString(), fontSize = 9.sp)
        }
        Spacer(Modifier.width(20.dp))

        Row(
            Modifier
                .clip(RoundedCornerShape(50))
                .clickable { /*Todo on comment clicked*/ }
                .padding(5.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Icon(
                imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment),
                contentDescription = "Comment"
            )
            Spacer(modifier = Modifier.width(5.dp))
            Text(text = commentsCount.toString(), fontSize = 9.sp)
        }


    }
}

@Composable
private fun PostTopView(
    category: String,
    onOptionsClicked: () -> Unit,
    onBookmarkClicked: () -> Unit
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween
    ) {

        Row(verticalAlignment = Alignment.CenterVertically) {
            IconButton(onClick = onOptionsClicked) {
                Icon(
                    imageVector = ImageVector.vectorResource(id = R.drawable.ic_threedots),
                    contentDescription = "Options",
                    tint = Color.Unspecified
                )
            }

            Text(text = category, fontSize = 16.sp, color = LightTextColor)

        }

        IconButton(onClick = onBookmarkClicked) {
            Icon(
                imageVector = ImageVector.vectorResource(id = R.drawable.ic_bookmark),
                contentDescription = "Bookmark"
            )
        }

    }
}

and the lazyColumn:

LazyColumn(contentPadding = paddingValues , state = state ) {
    item {
        Spacer(modifier = Modifier.height(10.dp))

        DoctorsList(
            viewModel.doctorListData.value,
            onCardClicked = {})
    }
    items(30) {  post ->
        HomePostView(
            category = "Public Health ",
            title = "Food Importance",
            subTitle = "you should eat every day it's healthy and important for you, and drink water every 2 hours and what you should do is you should run every day for an hour"
        )

    }
}

Note: I'm still not using a viewmodel i'm just testing the view with fake data

Upvotes: 46

Views: 11213

Answers (6)

Kashif Masood
Kashif Masood

Reputation: 351

If you are using JPGs or PNGs in your project then check for their size, images having larger size causes a lots of lagging issues on low end devices. I was having the same issue with my simple LazyColumn list and turned out I was using a JPG having size larger than 2MBs.

Upvotes: 0

Linh
Linh

Reputation: 60923

In my case, after I set the height of ComposeView to a specific value, it make LazyColumn scroll smooth.
Therefore, I create a XML file like

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

Fragment

override fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
    super.onViewCreated(view, savedInstanceState)
    view.doOnLayout {
        compose.layoutParams.height = view.height
        compose.requestLayout()
    }
}

I know ComposeView height already match_parent so set the height for it again on Fragment seem useless. However, without setting the height, LazyColumn will lagging when scroll.
I am not sure if it will work in all device but work well on my Pixel and Xiomi.

Upvotes: 0

AgentP
AgentP

Reputation: 7220

Ok, So far I know that there is an issue with the API when it comes to performance ...But what I found is this

Actually, In my case, I was just loading the image with 2980*3750 pixels image. I just crunched my resources to shorter pixels by some other tools

Now the lag is not present...

Upvotes: 0

T.H
T.H

Reputation: 19

TLDR: Make sure your new LazyColumn compose element is not within a RelativeLayout or LinearLayout.

After some investigation the solution to this issue for us was the view in which the LazyColumn was constrained.

Our project uses a combination of Jetpack Compose and the older XML layout files. Our new compose elements were embedded within a RelativeLayout of an existing XML file. This was where the problem was. The compose element would be given the entire screen and then the onMeasure function of the compose element was called to re-configure the view and add our bottom nav bar...this onMeasure was called over and over again, which also in the case of a LazyColumn the re-measuring was throwing out the cache as the height had changed.

The solution for us was to change the RelativeLayout that contained both the new compose element and the bottom nav bar and replace it with a ConstraintLayout. This prevented the onMeasure from being called more than twice and gave a massive performance increase.

Upvotes: 1

Vova
Vova

Reputation: 1091

Try to build release build with turn off debug logs. should be works fine.

Upvotes: 0

Brian Yencho
Brian Yencho

Reputation: 2958

This may not work for anyone else but on an earlier version (1.0.0-beta01) I saw a very large improvement in performance when I switched

lazy(items) { item ->
    ...
}

to

items.forEach { item ->
    lazy {
        ...
    }
}

I have no idea why and I'm not sure if this is still the case in later versions but its worth checking. So for the example given in the question, that would mean changing

items(30) {
    ...
}

to

repeat(30) {
    item { 
        ...
    }
}

Upvotes: 4

Related Questions