Skip to content

Commit 0390f5b

Browse files
update AnimatedZoomLayout with SubcomposeLayout to match content dimensions
SubcomposeLayout is used for getting content dimensions and passing these dimensions to AnimatedZoomState for valid pan bounding based on content size
1 parent 14fc32f commit 0390f5b

File tree

2 files changed

+69
-29
lines changed

2 files changed

+69
-29
lines changed

image/src/main/java/com/smarttoolfactory/image/DimensionSubcomposeLayout.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import androidx.compose.ui.unit.Constraints
1717
* some circumstances
1818
*
1919
* @param placeMainContent when set to true places main content. Set this flag to false
20-
* when dimensions of content is required for inside [mainContent]. Just measure it then pass
21-
* its dimensions to any child composable
20+
* when dimensions of content is required for [mainContent]. Measure [mainContent] then pass
21+
* its dimensions to any child composable but don't place it because we already place
22+
* it inside [dependentContent]
2223
*
2324
* @param mainContent Composable is used for calculating size and pass it
2425
* to Composables that depend on it
@@ -30,7 +31,7 @@ import androidx.compose.ui.unit.Constraints
3031
@Composable
3132
fun DimensionSubcomposeLayout(
3233
modifier: Modifier = Modifier,
33-
placeMainContent:Boolean = true,
34+
placeMainContent: Boolean = true,
3435
mainContent: @Composable () -> Unit,
3536
dependentContent: @Composable (Size) -> Unit
3637
) {
@@ -41,7 +42,7 @@ fun DimensionSubcomposeLayout(
4142
// Subcompose(compose only a section) main content and get Placeable
4243
val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
4344
.map {
44-
it.measure(constraints)
45+
it.measure(constraints.copy(minWidth = 0, minHeight = 0))
4546
}
4647

4748
// Get max width and height of main component
@@ -63,7 +64,7 @@ fun DimensionSubcomposeLayout(
6364

6465
layout(maxWidth, maxHeight) {
6566

66-
if(placeMainContent){
67+
if (placeMainContent) {
6768
mainPlaceables.forEach { placeable: Placeable ->
6869
placeable.placeRelative(0, 0)
6970
}

image/src/main/java/com/smarttoolfactory/image/zoom/AnimatedZoomLayout.kt

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,43 @@ package com.smarttoolfactory.image.zoom
22

33
import androidx.compose.foundation.border
44
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxSize
56
import androidx.compose.runtime.Composable
67
import androidx.compose.ui.Alignment
78
import androidx.compose.ui.Modifier
89
import androidx.compose.ui.graphics.Color
10+
import androidx.compose.ui.layout.Measurable
11+
import androidx.compose.ui.layout.Placeable
12+
import androidx.compose.ui.layout.SubcomposeLayout
913
import androidx.compose.ui.platform.LocalDensity
14+
import androidx.compose.ui.unit.Constraints
1015
import androidx.compose.ui.unit.DpSize
1116
import androidx.compose.ui.unit.dp
12-
import com.smarttoolfactory.image.DimensionSubcomposeLayout
17+
import com.smarttoolfactory.image.SlotsEnum
1318

19+
20+
/**
21+
* Layout that can zoom, rotate, pan its content with fling and moving back to bounds animation.
22+
*/
1423
@Composable
1524
fun AnimatedZoomLayout(
1625
modifier: Modifier = Modifier,
1726
content: @Composable () -> Unit
1827
) {
1928

20-
val density = LocalDensity.current
21-
22-
DimensionSubcomposeLayout(
23-
placeMainContent = false,
29+
AnimatedZoomSubcomposeLayout(
30+
modifier = modifier,
2431
mainContent = { content() }
2532
) {
2633
Box(
27-
modifier
34+
Modifier
35+
.fillMaxSize()
2836
.border(5.dp, Color.Red)
2937
.animatedZoom(
3038
animatedZoomState = rememberAnimatedZoomState(
3139
minZoom = .5f,
32-
maxZoom = 30f
40+
maxZoom = 30f,
41+
contentSize = it
3342
),
3443
),
3544
contentAlignment = Alignment.Center
@@ -39,25 +48,55 @@ fun AnimatedZoomLayout(
3948
}
4049
}
4150

51+
/**
52+
* SubcomposeLayout for getting dimensions of [mainContent] while laying out with [modifier]
53+
* Use of this layout is suitable when size of parent doesn't match content
54+
* and size [mainContent] is required inside [dependentContent] to use [mainContent] size
55+
* as reference or dimensions for child composables inside [dependentContent].
56+
*
57+
*/
4258
@Composable
43-
fun AnimatedZoomLayout2(
59+
private fun AnimatedZoomSubcomposeLayout(
4460
modifier: Modifier = Modifier,
45-
content: @Composable () -> Unit
61+
mainContent: @Composable () -> Unit,
62+
dependentContent: @Composable (DpSize) -> Unit
4663
) {
4764

48-
Box(
49-
modifier
50-
.border(4.dp, Color.Red)
51-
.animatedZoom(
52-
animatedZoomState = rememberAnimatedZoomState(
53-
minZoom = .5f,
54-
maxZoom = 30f,
55-
contentSize = DpSize(200.dp, 200.dp)
56-
)
57-
),
58-
contentAlignment = Alignment.Center
59-
) {
60-
content()
61-
}
65+
val density = LocalDensity.current
66+
67+
SubcomposeLayout(
68+
modifier = modifier
69+
) { constraints: Constraints ->
70+
71+
// Subcompose(compose only a section) main content and get Placeable
72+
val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
73+
.map {
74+
it.measure(constraints.copy(minWidth = 0, minHeight = 0))
75+
}
76+
77+
// Get max width and height of main component
78+
var maxWidth = 0
79+
var maxHeight = 0
6280

63-
}
81+
mainPlaceables.forEach { placeable: Placeable ->
82+
maxWidth += placeable.width
83+
maxHeight = placeable.height
84+
}
85+
86+
val dependentPlaceables: List<Placeable> = subcompose(SlotsEnum.Dependent) {
87+
val dpSize = density.run { DpSize(maxWidth.toDp(), maxHeight.toDp()) }
88+
dependentContent(dpSize)
89+
}
90+
.map { measurable: Measurable ->
91+
measurable.measure(constraints)
92+
}
93+
94+
layout(constraints.maxWidth, constraints.maxHeight) {
95+
96+
97+
dependentPlaceables.forEach { placeable: Placeable ->
98+
placeable.placeRelative(0, 0)
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)