How to obtain Android compose LazyVerticalGrid with consistent/standard item dimensions - android

My current Android project has a number of LazyVerticalGrid lists as follows:-
LazyVerticalGrid(
columns = GridCells.Fixed(LocalListColumnsComposition.current.value),
contentPadding = PaddingValues(0.dp),
verticalArrangement = Arrangement.spacedBy(0.dp),
horizontalArrangement = Arrangement.spacedBy(0.dp),
content = {
items(
items = state,
key = { myUI -> myUI.contentId }) { myUI ->
MyCard(myUI!!, onClick)
}
}
)
my issue is that my list items are different sizes due to their content, which results in a very confusing user experience.
e.g. some items have images, some have text that fills 1 line, others have text that wraps over multiple lines
What I would like is for each of my list items to be a fixed size and still show as much detail as possible
Will I have to measure all items before displaying them and calculate the "standard" dimensions required?
How can I achieve the desired result?
I do not want to use columns = GridCells.Adaptive(minSize = nnn.dp) as my lists have one column in portrait and multiple columns (depending on screen size) in landscape

Related

Adjust content to scrollable row width in jetpack compose

I have a Row with an horizontal scroll and n items inside of varying and unknown widths.
Case 1 -> In case the items dont fill the container's width I want the last item to fill the remaining space.
Case 2 -> In case the items sum of widths is higher than the containers width (hence allowing scroll), I want the last item to behave like all the others.
I have tried to use .fillMaxWidth in the last item but inside a scrollable row this has zero effect.
I can use .weight(1f) for Case 1, but this ruins the UI in Case 2.
I know a solution could be calculating manually the width of each item, and switch the modifier from adding the weight modifier to not using it as soon as the widths exceed the container's width but I believe there might be a solution using Modifier functions inside the Row? Or possibly with a Lazy Row?
Sample code:
val horizontalScroll = rememberScrollState()
Row(
Modifier
.fillMaxWidth()
.horizontalScroll(horizontalScroll)
) {
items.forEachIndexed { index, item ->
ItemComponent(
modifier = if (index == items.lastIndex) Modifier.weight(1f) else Modifier
text = item.text
)
}
}

Current scroll position value in pixels in LazyColumn Jetpack Compose

I want to have the total scroll position in px in LazyColumn.
The column itself has a ScrollState which has a value (Described in the document as the current scroll position value in pixels), But LazyColumn ScrollState hasn't this field, I want the exact equivalent of Column ScrollState scroll position value for LazyColumn, How I can achieve that with the help of LazyScrollState?
The reason it exists in Column is because it knows about all the items (rows) when it's being generated.
But LazyColumn only displays a portion of the total views at any given moment, and when you scroll, it continuously re-generates the views on the screen (plus a couple above and/or below the visible area), so it never actually calculates the total height of all the rows. If you wanted to get the scroll position, you would have to manually calculate it. If the height of each row is the same, it'll work pretty well. But if the heights are different, then you won't be able to get the exact scroll position (your calculations will fluctuate depending on the height of the rows that are currently displayed). But here's what you have to do to calculate it yourself:
Calculate the total size of all the items
val totalItems = lazyListState.layoutInfo.totalItemsCount
val itemLengthInPx = lazyListState.layoutInfo.visibleItemsInfo.firstOrNull()?.size ?: 0
val totalLengthInPx = totalItems * itemLengthInPx
Calculate the current scroll position
val scrollPos = (lazyListState.firstVisibleItemIndex * itemLengthInPx) / totalLengthInPx
But as I mentioned earlier, this calculation depends on the height of each item (itemLengthInPx), and it works perfectly if it's the same for all the views. But you can see how it'll fluctuate if each view has a different height
The scroll amount can be obtained, no calculation required, in the items themselves, by attaching an onGloballyPosition{ it.positionInParent()} modifier to one or more items.
Then, the items can do what they need to do with their own scroll position, such as offsetting some screen-drawing y coordinate.
Or, if you need the scroll offset in the parent LazyColumn, you could have one item (perhaps an item with zero height, though I've not tested that) at the very top of your list of items, that reports back its position to the parent (perhaps by updating a mutableState that was passed to the item by the parent) whenever it moves.
I had the same need and onGloballyPosition{ it.positionInParent()} addressed it very nicely.
You can get it by firstVisibleItemScrollOffset.
('androidx.activity:activity-compose:1.3.1')
val listState = rememberLazyListState()
val itemHeight = with(LocalDensity.current) { 80.dp.toPx() } // Your item height
val scrollPos = listState.firstVisibleItemIndex * itemHeight + listState.firstVisibleItemScrollOffset
Text(text = "scroll: $scrollPos")
LazyColumn(state = listState) {
// Your items here
}
Also, you can set the scroll position to listState, like this:
LaunchedEffect(key1 = "Key") {
delay(1000) // To show scroll explicitly, set the delay
listState.scrollBy(itemHeight * 2)
}

Jetpack Compose: Equal width columns of dynamic content

I have some dynamic content and I need to render it as a LazyRow of several Columns. Each Column can have Texts of various lengths.
Ideally, I would like every Column to be the size of the largest Column.
How can I ensure that every Column is the same width?
I think I need IntrinsicSize here but I'm not sure how to apply it in my case:
LazyRow {
items(items = steps) {
Column {
// ... dynamic content, including Texts of varying sizes and lengths
// How can I ensure that EVERY column is the SAME width?
}
}
}

How do I expand the full height of a lazy column in Jetpack Compose?

I have a Lazy column like so
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.height(gridMultiple(i = 15) * data.size)
.padding(vertical = gridMultiple(i = 2)),
verticalArrangement = Arrangement.spacedBy(gridMultiple(i = 2)),
userScrollEnabled = false
) {
items(data.size) { index ->
val item = data[index]
ListItem(
item,
editButtonClick = { id -> onEditClick(id) }
)
}
}
and it is encapsulated in an interop composable XML element like so:
<androidx.compose.ui.platform.ComposeView
android:id="#+id/informationList"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
The catch is that the informationList is inside of a ScrollView (XML), and I want the lazy column list to be fully expanded within the scroll view. But I do not know the height that the elements need to be, so I am doing this math here:
.height(gridMultiple(i = 15) * data.size)
But it uses either too much space or not enough. So either elements get cut off, or they have a lot of empty space at the end. I want to use exactly as much space as the LazyColumn needs. If I set the height to be wrapping, I get an exception that the lazy column has an infinite possible height and must have a height specified. There must be a way to do this. I know that the max amount of elements that can ever be in the lazy column is limited to about 10.
That will defeat the whole purpose of LazyColumn. The lazy part just won't work when you use it like that. You could just go with normal Column and it would make no difference at all in terms of performance. Especially with 10 items, with such number the LazyColumn is huge overkill. Bonus for your case - Column supports wrapContentHeight().
TLDL - just use Column with wrapContentHeight() and it should be just fine

What has replaced weights?

Trying to figure out how to position items in the latest build... Many examples online indicate to use modifier = Modifier.weight(...), but the option is simply not given as of the build I'm using.
I need to send a button at the bottom of the page regardless of the size of a lazy column just above it. What's the preferred way to do such a thing?
With 1.0.0-beta02 the Modifier.weight exists.
Size the element's height proportional to its weight relative to other weighted sibling elements in the Column. The parent will divide the vertical space remaining after measuring unweighted child elements and distribute it according to this weight
Something like:
val itemsList = (0..60).toList()
Column {
LazyColumn(modifier = Modifier.weight(1f)){
items(itemsList) {
Text("Item is $it")
}
}
Row( verticalAlignment = Alignment.Bottom) {
Text(text = "Footer row")
}
}
Otherwise you can use a ConstraintLayout.

Categories

Resources