@@ -52,17 +52,22 @@ import androidx.compose.material3.Text
5252import androidx.compose.runtime.Composable
5353import androidx.compose.runtime.DisposableEffect
5454import androidx.compose.runtime.LaunchedEffect
55+ import androidx.compose.runtime.MutableState
5556import androidx.compose.runtime.getValue
5657import androidx.compose.runtime.mutableStateOf
5758import androidx.compose.runtime.remember
5859import androidx.compose.runtime.setValue
5960import androidx.compose.ui.Alignment
6061import androidx.compose.ui.Modifier
6162import androidx.compose.ui.draw.scale
63+ import androidx.compose.ui.geometry.Offset
6264import androidx.compose.ui.graphics.Color
6365import androidx.compose.ui.graphics.asImageBitmap
66+ import androidx.compose.ui.layout.boundsInWindow
67+ import androidx.compose.ui.layout.onGloballyPositioned
6468import androidx.compose.ui.platform.LocalConfiguration
6569import androidx.compose.ui.platform.LocalContext
70+ import androidx.compose.ui.platform.LocalDensity
6671import androidx.compose.ui.tooling.preview.Preview
6772import androidx.compose.ui.unit.dp
6873import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -91,6 +96,7 @@ fun CameraScreen(
9196) {
9297 var previewView by remember { mutableStateOf<PreviewView ?>(null ) }
9398 val pageIds by viewModel.pageIds.collectAsStateWithLifecycle()
99+ val thumbnailCoords = remember { mutableStateOf(Offset .Zero ) }
94100
95101 val captureController = remember { CameraCaptureController () }
96102 DisposableEffect (Unit ) {
@@ -124,7 +130,9 @@ fun CameraScreen(
124130 pageIds = pageIds,
125131 imageLoader = { id -> viewModel.getBitmap(id) },
126132 onPageClick = { index -> viewModel.navigateTo(Screen .FinalizeDocument (index)) },
127- listState = listState
133+ listState = listState,
134+ onLastItemPosition =
135+ { coords -> thumbnailCoords.value = coords.localToWindow(Offset .Zero ) }
128136 )
129137 },
130138 cameraUiState = CameraUiState (pageIds.size, liveAnalysisState, captureState),
@@ -136,6 +144,7 @@ fun CameraScreen(
136144 )
137145 },
138146 onFinalizePressed = onFinalizePressed,
147+ thumbnailCoords = thumbnailCoords,
139148 )
140149}
141150
@@ -146,6 +155,7 @@ private fun CameraScreenScaffold(
146155 cameraUiState : CameraUiState ,
147156 onCapture : () -> Unit ,
148157 onFinalizePressed : () -> Unit ,
158+ thumbnailCoords : MutableState <Offset >,
149159) {
150160 Box {
151161 Scaffold (
@@ -172,12 +182,12 @@ private fun CameraScreenScaffold(
172182 )
173183 }
174184 }
175- CapturedImage (cameraUiState)
185+ CapturedImage (cameraUiState, thumbnailCoords )
176186 }
177187}
178188
179189@Composable
180- private fun CapturedImage (cameraUiState : CameraUiState ) {
190+ private fun CapturedImage (cameraUiState : CameraUiState , thumbnailCoords : MutableState < Offset > ) {
181191 cameraUiState.captureState.processedImage?.let { image ->
182192 Surface (
183193 color = Color .Black .copy(alpha = 0.3f ),
@@ -190,8 +200,9 @@ private fun CapturedImage(cameraUiState: CameraUiState) {
190200 isAnimating = true
191201 }
192202 val transition = updateTransition(targetState = isAnimating, label = " captureAnimation" )
193- val targetOffsetX = 0 .dp // TODO real value
194- val targetOffsetY = 200 .dp // TODO real value
203+ val density = LocalDensity .current
204+ var targetOffsetX by remember { mutableStateOf(0 .dp) }
205+ var targetOffsetY by remember { mutableStateOf(0 .dp) }
195206
196207 val offsetX by transition.animateDp(
197208 transitionSpec = { tween(durationMillis = ANIMATION_DURATION ) },
@@ -206,15 +217,29 @@ private fun CapturedImage(cameraUiState: CameraUiState) {
206217 label = " scale"
207218 ) { if (it) 0.3f else 1f }
208219
209- Image (
210- bitmap = image.asImageBitmap(),
211- contentDescription = null ,
212- modifier = Modifier
213- .fillMaxSize()
214- .padding(24 .dp)
215- .offset(x = offsetX, y = offsetY - 100 .dp)
216- .scale(scale)
217- )
220+
221+ val justABitToTheTop = 100 .dp
222+ Box (modifier = Modifier
223+ .onGloballyPositioned { coordinates ->
224+ val bounds = coordinates.boundsInWindow()
225+ val centerX = bounds.left + bounds.width / 2
226+ val centerY = bounds.top + bounds.height / 2
227+ with (density) {
228+ targetOffsetX = thumbnailCoords.value.x.toDp() - centerX.toDp() + PAGE_LIST_ELEMENT_SIZE_DP .dp
229+ targetOffsetY = thumbnailCoords.value.y.toDp() - centerY.toDp() + justABitToTheTop
230+ }
231+ }
232+ ) {
233+ Image (
234+ bitmap = image.asImageBitmap(),
235+ contentDescription = null ,
236+ modifier = Modifier
237+ .fillMaxSize()
238+ .padding(24 .dp)
239+ .offset(x = offsetX, y = offsetY - justABitToTheTop)
240+ .scale(scale)
241+ )
242+ }
218243 }
219244}
220245
@@ -333,6 +358,7 @@ fun CameraScreenPreviewWithProcessedImage() {
333358private fun ScreenPreview (captureState : CaptureState ) {
334359 val context = LocalContext .current
335360 MyScanTheme {
361+ val thumbnailCoords = remember { mutableStateOf(Offset .Zero ) }
336362 CameraScreenScaffold (
337363 cameraPreview = {
338364 Box (
@@ -356,12 +382,13 @@ private fun ScreenPreview(captureState: CaptureState) {
356382 }
357383 },
358384 onPageClick = {},
359- listState = LazyListState ()
385+ listState = LazyListState (),
360386 )
361387 },
362388 cameraUiState = CameraUiState (pageCount = 4 , LiveAnalysisState (), captureState),
363389 onCapture = {},
364390 onFinalizePressed = {},
391+ thumbnailCoords = thumbnailCoords,
365392 )
366393 }
367394}
0 commit comments