abbic
06/07/2024, 5:12 PMabbic
06/07/2024, 5:13 PMStylianos Gakis
06/07/2024, 7:24 PMabbic
06/07/2024, 7:57 PMStylianos Gakis
06/07/2024, 8:48 PMabbic
06/10/2024, 11:01 AMif (space taken <= space available)
fill children
if (space taken > space available)
don't modify child size and instead scroll
Stylianos Gakis
06/10/2024, 12:08 PMStylianos Gakis
06/10/2024, 12:09 PMThisCustomScrollableRow {
Item(...)
Item(...)
FooComposable()
...
...
}}
abbic
06/10/2024, 12:12 PMScrollableTabRow(listOfTabDatas, selected, onTabSelected)
abbic
06/10/2024, 12:13 PMabbic
06/10/2024, 12:35 PMRowOfTabs(
modifier = Modifier
.background(Color.Red)
.padding(vertical = DP_4),
) {
tabs.forEachIndexed { index, tab ->
TabItem(
leadingIcon = tab.leadingIcon,
title = tab.title,
onSelect = {
onSelect(it)
},
selected = selectedIndex == index,
index = index
)
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun RowOfTabs(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
val scrollState = rememberScrollState()
val overscrollEffect = ScrollableDefaults.overscrollEffect()
// dont mind this, just experimenting
SubcomposeLayout(
Stylianos Gakis
06/10/2024, 1:07 PMStylianos Gakis
06/10/2024, 1:10 PM&& maxIntrinsicWidthTaken != 0
check in there, for if all the items are still empty, but I assume this should not be possible at all in your case anyway.
In any case, you should probably be able to grab this and move forward towards what you want to achieve.
AbbicItem
is gonna be your individual tab item which actually makes use of the index for selected and the onClick and such
@Composable
fun AbbicLayout(
listOfTabDatas: NonEmptyList<String>,
selected: Int,
onTabSelected: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
var layoutCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
Box(
modifier = modifier
.fillMaxWidth()
.onPlaced { layoutCoordinates = it }
.horizontalScroll(rememberScrollState()),
) {
if (layoutCoordinates != null) {
AbbicLayout(
componentSize = layoutCoordinates!!.size,
listOfTabDatas = listOfTabDatas,
selected = selected,
onTabSelected = onTabSelected,
)
}
}
}
@SuppressLint("ModifierParameter")
@Composable
fun AbbicLayout(
componentSize: IntSize,
listOfTabDatas: NonEmptyList<String>,
selected: Int,
onTabSelected: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
Layout(
content = {
for (data in listOfTabDatas) {
AbbicItem(data)
}
},
modifier = modifier
) { measurables, constraints ->
val realLayoutWidth = componentSize.width
val maxIntrinsicWidthTaken = measurables.fastSumBy { measurable ->
measurable.maxIntrinsicWidth(constraints.maxHeight)
}
val itemConstraints: Constraints
if (maxIntrinsicWidthTaken < realLayoutWidth && maxIntrinsicWidthTaken != 0) {
val fixedWidthPerItem = realLayoutWidth / measurables.size
itemConstraints = constraints.copy(minWidth = fixedWidthPerItem, maxWidth = fixedWidthPerItem)
} else {
itemConstraints = constraints.copy(minWidth = 0, maxWidth = constraints.maxWidth)
}
val placeables = measurables.fastMap { measurable ->
measurable.measure(itemConstraints)
}
layout(realLayoutWidth, placeables.fastMaxOfOrNull { it.height } ?: 0) {
var x = 0
placeables.fastMap {
it.place(x, 0)
x += it.width
}
}
}
}
@Composable
fun AbbicItem(itemContent: String, modifier: Modifier = Modifier) {
val color = (0xFF000000..0xFFFFFFFF).random()
Text(itemContent, modifier.background(Color(color = color)))
}
Stylianos Gakis
06/10/2024, 1:10 PM@Preview(device = "id:pixel_8")
@Composable
private fun PreviewAbbic() {
HedvigTheme {
Surface(color = MaterialTheme.colorScheme.background) {
val listOfTabDatas = nonEmptyListOf(
"Home", "Books", "Profile",
"Home", "Books", "Profile",
"Home", "Books", "Profile",
)
AbbicLayout(
listOfTabDatas = listOfTabDatas,
selected = 0,
onTabSelected = {},
)
}
}
}
You can do the same to see it in action.
If you don't use arrow, just replace NonEmptyList
with just List
abbic
06/10/2024, 1:21 PMStylianos Gakis
06/10/2024, 1:33 PMabbic
06/10/2024, 1:35 PMjava.lang.IllegalArgumentException: Can't represent a size of 715827970 in Constraints at androidx.compose.ui.unit.Constraints$Companion.bitsNeedForSize(Constraints.kt:403) at androidx.compose.ui.unit.Constraints$Companion.createConstraints-Zbe2FdA$ui_unit_release(Constraints.kt:366) at androidx.compose.ui.unit.ConstraintsKt.Constraints(Constraints.kt:433) at androidx.compose.ui.unit.ConstraintsKt.Constraints$default(Constraints.kt:418)
i think i may be misusing constraints though so lets forget about it for nowabbic
06/10/2024, 1:35 PMabbic
06/10/2024, 1:36 PMval excessSpaceSize = constraints.maxWidth - minWidth
val excessSpacePerPlaceable = excessSpaceSize / mainPlaceables.size
placeable.measure(
Constraints(
minWidth = excessSpacePerPlaceable + mainPlaceables[index].width
)
)
Stylianos Gakis
06/10/2024, 1:37 PMrealLayoutWidth
as constraints.maxWidth
will be infinite inside there since you're inside a horizontally scrollable containerabbic
06/10/2024, 1:38 PMStylianos Gakis
06/10/2024, 1:41 PMLayout(
content = {
for (data in listOfTabDatas) {
Text(data)
}
},
modifier = modifier,
) { measurables, constraints ->
val realLayoutWidth = componentSize.width
val maxIntrinsicWidths = measurables.fastMap { measurable ->
measurable.maxIntrinsicWidth(constraints.maxHeight)
}
val maxIntrinsicWidthTaken = maxIntrinsicWidths.sum()
val leftoverWidth = realLayoutWidth - maxIntrinsicWidthTaken
val measurablesSize = measurables.size
val placeables = measurables.fastMapIndexed { index, measurable ->
val itemConstraints: Constraints = if (maxIntrinsicWidthTaken < realLayoutWidth && maxIntrinsicWidthTaken != 0) {
val fixedExtraWidth = (realLayoutWidth - leftoverWidth) / measurablesSize
val fixedWidth = maxIntrinsicWidths[index] + fixedExtraWidth
constraints.copy(minWidth = fixedWidth, maxWidth = fixedWidth)
} else {
constraints.copy(minWidth = 0, maxWidth = constraints.maxWidth)
}
measurable.measure(itemConstraints)
}
layout(realLayoutWidth, placeables.fastMaxOfOrNull { it.height } ?: 0) {
var x = 0
placeables.fastMap {
it.place(x, 0)
x += it.width
}
}
}
Smth like this?
Deleted my test code so I couldn't preview now, but this is where I'd start I thinkabbic
06/10/2024, 1:44 PMabbic
06/10/2024, 2:06 PMabbic
06/10/2024, 2:07 PMStylianos Gakis
06/10/2024, 2:12 PMabbic
06/10/2024, 2:44 PMStylianos Gakis
06/10/2024, 3:00 PMabbic
06/10/2024, 3:01 PMabbic
06/10/2024, 3:01 PMStylianos Gakis
06/10/2024, 3:02 PMlayout()
block I think I did it wrong in the snippet I shared.
Look at what I do here https://github.com/HedvigInsurance/android/blob/0ea7547d4e2271b28cb6c0ff1067719d99[…]in/kotlin/com/hedvig/android/feature/home/home/ui/HomeLayout.kt where the layout itself does need to be bigger than just the constraints if there are enough items. You'll probably need to do the same so that the contents are actually scrollableStylianos Gakis
06/10/2024, 3:03 PMabbic
06/10/2024, 3:03 PMabbic
06/10/2024, 3:03 PMabbic
06/10/2024, 4:04 PM