Jetpack Compose Lazy Table - android

My current goal is to achieve view that can represent a table, but this table can contain pretty much elements in its columns and rows. My current idea is to populate LazyColumn with LazyRows.
#Composable
internal fun Table() {
LazyColumn {
items(count = 100) {
LazyRow {
items(10) {
Box(
modifier = Modifier
.size(100.dp)
.border(width = 1.dp, color = Color.Black)
)
}
}
}
}
}
But there is a huge problem: i don't want the rows to be able to scroll by themselves, i want any scrolling to be shared. Unfortunately, i can't figure out gow to achieve that. As far as i know lazy grid can't help me either.
I also tried to use same instance of LazyListState, but it doesn't work.

In a new version appered new layout called "LazyLayout" - it's somewhat of your custom lazy layout builder. This should solve my problem, although i haven't tried it yet

Related

The title won't minimize dialog in JetPack Compose BottomSheetDialog with rememberNestedScrollInteropConnection

I have BottomDialog Fragment which consists only of Composable (Row(title) and LazyColumn).
The first issue I faced was when you scroll your list down and then you try to scroll up list won't scroll and the dialog starts to minimize. This is solved with
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
But now user can't minimize a dialog when he tries to do it by touching a title. And there is my question, How to solve this?
Minimum reproducible code
During creating this example I found that I can maximize dialog when touching the title, also I can start moving the action going to the top with my finger (to start to expand it) and then move the finger to the bottom of the screen, in this way the dialog will be dismissed, but still can't minimize it in a non-tricky way.
ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Theme {
Column(modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
Row {
Text(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
text = "Title"
)
}
LazyColumn(
Modifier
.weight(1f)
.fillMaxWidth()
) {
items(100) {
Text(
text = "Item $it",
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
)
}
}
}
}
}
}
Please do not propose BottomSheetScaffold. Because I want to create a standardized bottom dialog. And to use it I will need just pass a list of items and a Compose function for one item.
IMO BottomSheetScaffold shouldn't be released at all, cause it was designed "not well". Just imagine earlier before Jetpack Compose you write your code around the bottom dialog, nice layering. No.
As a temp decision. I just think of LazyColumn works properly so I need to wrap my header to the LazyColumn.
So I created this function. And Just pass here any Composable
#Composable
fun TrickyLazyColumn(content: #Composable () -> Unit) {
LazyColumn {
items(
items = listOf(1),
itemContent = {
content.invoke()
})
}
}

Why is it laggy when I scroll this list

I am trying to show a list of installed apps and a checkbox next to each app. The scrolling is terrible, it's noticably laggy. It looks like i am doing something wrong but i can't figure it out. how can I improve it?
#Composable
fun AppList(infoList: MutableList<android.content.pm.ResolveInfo>) {
val ctx = LocalContext.current
LazyColumn {
items(infoList) { info ->
var isChecked by remember { mutableStateOf(false) }
var icon by remember { mutableStateOf(info.loadIcon(ctx.packageManager)) }
var label by remember { mutableStateOf( info.loadLabel(ctx.packageManager).toString()) }
Row(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()
) {
Image(
painter = rememberImagePainterMine(icon),
contentDescription = label,
contentScale = ContentScale.Fit,
modifier = Modifier.padding(16.dp)
)
// Display the app name
Text(
text =label,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f)
)
// Display the checkbox on the right
Box(modifier = Modifier.clickable { isChecked = !isChecked }) {
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it },
modifier = Modifier.padding(16.dp)
)
}
}
}
}
}
#Composable
fun rememberImagePainterMine(drawable: Drawable): Painter = remember(drawable) {
object : Painter() {
override val intrinsicSize: Size
get() = Size(drawable.intrinsicWidth.toFloat(), drawable.intrinsicHeight.toFloat())
override fun DrawScope.onDraw() {
drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt())
drawable.draw(this.drawContext.canvas.nativeCanvas)
}
}
}
Are you running your app in release mode? It makes a big difference as to how smooth Jetpack Compose will be (due to the amount of debug code under the hood).
Google themselves explain it as follows:
If you're finding performance issues, make sure to try running your app in release mode. Debug mode is useful for spotting lots of problems, but it imposes a significant performance cost, and can make it hard to spot other code issues that might be hurting performance. You should also use the R8 compiler to remove unnecessary code from your app. By default, building in release mode automatically uses the R8 compiler.
Additionally, you should consider using keys with your LazyColumn to help Compose figure out if your data moves around or changes completely.
Without your help, Compose doesn't realize that unchanged items are just being moved in the list. Instead, Compose thinks the old "item 2" was deleted and a new one was created, and so on for item 3, item 4, and all the way down. The result is that Compose recomposes every item on the list, even though only one of them actually changed.
The solution here is to provide item keys. Providing a stable key for each item lets Compose avoid unnecessary recompositions. In this case, Compose can see that the item now at spot 3 is the same item that used to be at spot 2. Since none of the data for that item has changed, Compose doesn't have to recompose it.

How can I use LazyColumn in LazyColumn in jetpack compose?

I got this error message, And I don't get it.
java.lang.IllegalState
Exception: Vertically scrollable component was measured with an
infinity maximum height constraints, which is disallowed. One of the
common reasons is nesting layouts like LazyColumn and
Column(Modifier.verticalScroll()). If you want to add a header before
the list of items please add a header as a separate item() before the
main items() inside the LazyColumn scope. There are could be other
reasons for this to happen: your ComposeView was added into a
LinearLayout with some weight, you applied
Modifier.wrapContentSize(unbounded = true) or wrote a custom layout.
Please try to remove the source of infinit
e constraints in the hierarchy above the scrolling container.
Here's example code.
#Composable
fun SupplementSearchScreen(
onSearch: (String) -> List<Vitamin>,
updateRecentAddedSupplementList: List<Vitamin>
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
) {
item{
SupplementSearchResultCard(someList)
}
}
}
#Composable
fun SupplementSearchScreen(list:List<SomeList>){
ContentCard(
modifier = Modifier.fillMaxWidth()
) {
Text(
text = "Hello World",
fontSize = 16.sp,
color = Color.Black
)
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
items(list){ resultItem ->
SearchResultItem(resultItem)
}
}
}
}
What's wrong here?
Lazy column needs fixed height component as parent to achieve lazy behavior,
if add lazy column inside parent with modifier Modifier.verticalScroll() or another Lazy column it will get infinite height it wont be able to decide when to compose and when to dispose an composable, instead use items{} of block of lazy column
in xml also same issue appears if wrap recyclerview inside nested scroll view but in xml instead of showing error it cause ANR as items increases in Recycler view adapter
You can use:
Modifier.height(LocalConfiguration.current.screenHeightDp.dp)
on the first lazy column to get all screen height.
I don't know if something like this will work for you but just to try it out.
You can't directly use LazyColumn inside LazyColumn. Just use LazyColumn with different types like this:
sealed interface Item {
data class SearchCard(val name: String) : Item
data class SearchText(val name: String): Item
}
#Composable
fun SearchScreen(list: List<Item>) {
LazyColumn {
items(list.size) { index ->
when (list[index]) {
is Item.SearchCard -> TODO()
is Item.SearchText -> TODO()
}
}
}
}

LazyColumn to not recompose some items

With RecyclerView, I can make some ViewHolder not recyclable (follow some answers in
I want my RecyclerView to not recycle some items).
Can I make LazyColumn to not recompose some items (similar to make RecyclerView don't recycle some ViewHolder)? I have few items in LazyColumn with some big images, it recompose after scrolling down and up so scroll is not smooth.
I met the same problem and use Column instead with a modifier vertical scroll. If you don't want it recycle view, just load all ( few items)
Column(
modifier = Modifier
.constrainAs(listView) {
top.linkTo(
parent.top
)
}
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
list.forEachIndexed { index, itemModel ->
ItemView(itemModel, index) {
// on item click
}
}
Spacer(modifier = Modifier.height(40.dp))
}
I had a similar issue with a composable that is heavily manipulating bitmaps and then draw them on a canvas. You noticed that processing while scrolling the lazyColumn up and down.
To face this issue a stored my manipulated bitmaps in a List<ImageBitmap> as rememberSaveable
val rememberFruits by rememberSaveable(images) {
mutableStateOf(doBitmapOperations(images))
}
Canvas(
modifier = Modifier
.fillMaxWidth()
.height(height)
.constrainAs(circle) {}
) {
rememberFruits
.forEach { createScaledImageBitmap ->
val (image, offset) = createScaledImageBitmap
drawImage(
image = image,
topLeft = offset
)
}
}
My item in the lazyColumn was still recomposed but the heavy bitmap operations were no more executed while scrolling and making scrolling up and down smooth.
hope it helps!

Jetpack Compose LazyVerticalGrid stuttering

I'm migrating my app and for the grid view I used LazyVerticalGrid that loads images using coilPainter but the scrolling stutters a lot and it feels laggy. Does anyone have a solution or a better implementation for this?
val photos: List<Photo>? by photosViewModel.photosLiveData.observeAsState(null)
Surface(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier.fillMaxSize()
) {
LazyVerticalGrid(
cells = GridCells.Fixed(3)
) {
photos?.let { photosList ->
items(photosList) { photo ->
Image(
modifier = Modifier.padding(2.dp).clickable {
photosViewModel.onPhotoClicked(photo)
},
painter = rememberCoilPainter(photo.url),
contentDescription = stringResource(R.string.cd_details_photo),
contentScale = ContentScale.Fit,
alignment = Alignment.Center,
)
}
}
}
}
}
Update
Trying to move rememberCoilPainter above Image like val painter = rememberCoilPainter(photo.url) and use the painter inside the Image did not work
items takes an additional parameter key, assign this parameter to a unique value like the index of each photo of your list, If you don't know how does that relate to scrolling jank? Or what the heck am I talking about? check out the following documentation for a detailed explanation:
https://developer.android.com/jetpack/compose/lifecycle#add-info-smart-recomposition
After updating my compose image loading library to coil-compose I was forced to set a size to either the request builder or the image itself. The problem of the stuttering was caused by allowing the photo to load the original size and thus recomposing was done multiple times and loading the image had to wait for the picture to load. By setting e fixed height to the Image fixed the issue because the Images() were created and the recomposition was made only for the picture inside not for the Image itself with different height.

Categories

Resources