fathony
12/07/2023, 4:25 AMfathony
12/07/2023, 1:15 PM<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF9800"
android:padding="8dp"
android:text="Hello world"
android:textColor="#FFFFFF"
android:textSize="24sp" />
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
and then on the code I can have something like so to calculate the height of the text
and then applying the height to padding top of list
.
text.doOnLayout {
if (list.paddingTop == 0) {
list.updatePadding(top = it.measuredHeight)
}
}
Here is something I come up with in Jetpack Compose:
@Composable
fun SampleApp() {
var searchBarHeight by remember { mutableStateOf(0.dp) }
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
Scaffold { paddingValues ->
val finalPaddingValues = PaddingValues(
start = paddingValues.calculateStartPadding(layoutDirection),
top = paddingValues.calculateTopPadding() + searchBarHeight + 16.dp, // 16.dp because we apply padding 8.0 into DockedSearchBar
end = paddingValues.calculateEndPadding(layoutDirection),
bottom = paddingValues.calculateBottomPadding()
)
Box(
modifier = Modifier
.fillMaxSize()
) {
DockedSearchBar(
... // irrelevant properties
modifier = Modifier
.padding(paddingValues)
.fillMaxWidth()
.padding(8.dp)
.onSizeChanged { searchBarHeight = with(density) { it.height.toDp() } }
) {
}
LazyColumn(
contentPadding = finalPaddingValues,
modifier = Modifier
.fillMaxSize()
) {
... // irrelevant
}
}
}
}
I was wondering if this is the best practice do this? Do you have any suggestions to improve? Thanks! 🙂Zach Klippenstein (he/him) [MOD]
12/07/2023, 3:28 PMPaddingValues
is an interface. You can create a single remembered instance of it that performs your calculations in the functions that actually return the individual values. Then you can skip the recomposition since only the padding modifier’s layout will be invalidated.fathony
12/12/2023, 2:20 AMDockedSearchBar
with Surface
and provide a button to change the height of Surface
on button press, it will recompose LazyColumn
since we cannot avoid recomposition because of padding changes. The Surface
will also be recomposed because the height property changes on Modifier
. 🤔Zach Klippenstein (he/him) [MOD]
12/12/2023, 4:33 PMfathony
12/13/2023, 4:19 AM@Composable
fun SampleApp() {
var surfaceHeight by remember { mutableStateOf(64.dp) }
val contentItems = List(1000) { "Item-$it" }
Scaffold { paddingValues ->
val lazyFinalPaddingValues = remember {
LazyPaddingValues(
base = { paddingValues },
surfaceBarHeightProvider = { surfaceHeight }
)
}
Box(
modifier = Modifier
.fillMaxSize()
) {
LazyColumn(
contentPadding = lazyFinalPaddingValues,
modifier = Modifier
.fillMaxSize()
) {
items(contentItems) { item ->
Text(
item,
modifier = Modifier
.clickable { }
.fillMaxWidth()
.height(48.dp)
.padding(horizontal = 12.dp)
.wrapContentHeight()
)
}
}
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.fillMaxWidth()
.height(surfaceHeight + paddingValues.calculateTopPadding())
) {
Text(
"Sample app",
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier
.padding(top = paddingValues.calculateTopPadding())
.fillMaxSize()
.padding(horizontal = 12.dp)
.wrapContentHeight()
)
}
IconButton(
onClick = {
surfaceHeight = (36..102).random().dp
},
colors = IconButtonDefaults.filledIconButtonColors(),
modifier = Modifier
.align(Alignment.BottomEnd)
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom))
.padding(8.dp)
) {
Icon(Icons.Rounded.Refresh, contentDescription = "Refresh")
}
}
}
}
class LazyPaddingValues(
private val base: () -> PaddingValues,
private val surfaceBarHeightProvider: () -> Dp
) : PaddingValues {
override fun calculateBottomPadding(): Dp = base().calculateBottomPadding()
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp =
base().calculateLeftPadding(layoutDirection)
override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp =
base().calculateRightPadding(layoutDirection)
override fun calculateTopPadding(): Dp =
base().calculateBottomPadding() + surfaceBarHeightProvider()
}
but as I check the layout inspector, the LazyColumn
still recompose when the surfaceHeight
changes. 🤔
Is there something wrong that I did in the code?fathony
12/13/2023, 4:22 AMZach Klippenstein (he/him) [MOD]
12/13/2023, 5:34 PMsurfaceHeight
in composition, on line 38Zach Klippenstein (he/him) [MOD]
12/13/2023, 5:37 PMModifier
.layout { measurable, constraints ->
val childConstraints = constraints.copy(minHeight = surfaceHeight, maxHeight = surfaceHeight)
val placeable = measurable.measure(childConstraints)
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
}
}
fathony
12/14/2023, 3:39 AM