androidkotlinandroid-jetpack-composepdfrendererandroid-jetpack-compose-lazy-column

Zooming each image inside LazyColumn causes content to overlap


I am trying to zoom images which are items inside my LazyColumn but when i try to zoom in it overlaps content with the other images and the scrolling of lazycolumn also becomes too difficult

 var scale by remember { mutableStateOf(1f) }
 var offset by remember { mutableStateOf(Offset(0f, 0f)) }

LazyColumn() {
                            items(mPdfRenderer?.pageCount!!) { message ->
Box(
                                    modifier = Modifier
                                        .fillMaxSize()
                                        .pointerInput(Unit) {
                                            detectTransformGestures { _, pan, zoom, _ ->
                                                scale *= zoom
                                                offset = Offset(
                                                    (offset.x + pan.x).coerceIn(
                                                        -200.dp.toPx(),
                                                        200.dp.toPx()
                                                    ),
                                                    (offset.y + pan.y).coerceIn(
                                                        -200.dp.toPx(),
                                                        200.dp.toPx()
                                                    )
                                                )
                                            }
                                        }
                                ) {
                                    Image(
                                        bitmap = bitmap!!.asImageBitmap(),
                                        contentDescription = "some useful description",
                                        modifier = Modifier
                                            .fillMaxWidth()
                                            .border(width = 1.dp, color = Color.Gray)
                                            .scale(scale)
                                            .offset(offset.x.dp, offset.y.dp)
                                            .aspectRatio(1f)
                                    )
                                }
                            }
                        }

I am actually creating a pdf viewer. How can the zooming be improved so that content is not overlapped and the vertical scrolling remains smooth?

Check this to understand the zooming issue


Solution

  • The issue in your code is you are keeping single

     var scale by remember { mutableStateOf(1f) }
     var offset by remember { mutableStateOf(Offset(0f, 0f)) }
    

    which applies to every item in LazyColumn while each pages should have its own scale and offset properties.

    private class PdfProperties() {
        var zoom = mutableStateOf(1f)
        var offset = mutableStateOf(Offset.Zero)
    }
    

    or

    val offsetMap = remember {
        mutableStateMapOf<Int, Offset>()
    }
    
    val scaleMap = remember {
        mutableStateMapOf<Int, Float>()
    }
    

    then you should apply and change offset and scale for each page.

    This is how you should implement it with the Modifier which clips PDF page as in this ZoomableList.

    https://stackoverflow.com/a/72668732/5457853

    For smooth scrolling you should consume PointerInputChange conditionally when number of fingers is bigger than 1 or zoom is bigger than 1f as in answer below.

    https://stackoverflow.com/a/76021552/5457853

    This custom gesture i wrote that is available in link above or as library here makes it easy to consume PointerInputChange conditionally as

                Modifier.pointerInput(Unit) {
    
                    //zoom in/out and move around
                    detectTransformGestures(
                        pass = PointerEventPass.Initial,
                        onGesture = { gestureCentroid: Offset,
                                      gesturePan: Offset,
                                      gestureZoom: Float,
                                      _,
                                      _,
                                      changes: List<PointerInputChange> ->
    
    
    
    // 🔥Consume touch when multiple fingers down
    // or zoom > 1f
    // This prevents LazyColumn scrolling
                            val size = changes.size
                            if (size > 1) {
                                changes.forEach { it.consume() }
                            }
                        }
                    )
                })
    

    Also you can refer this answer too.

    How to handle horizontal scroll gesture combined with transform gestures in Jetpack Compose

    Extra

    As i asked in comments if you want to limit zoom into Image you should also call Modifier.clipToBounds if you wish to limit zoom inside Box also you might want to change zoom center and empty area by limiting pan in limits with

    detectTransformGestures(
        onGesture = { _, gesturePan, gestureZoom, _ ->
    
            zoom = (zoom * gestureZoom).coerceIn(1f, 3f)
            val newOffset = offset + gesturePan.times(zoom)
    
            val maxX = (size.width * (zoom - 1) / 2f)
            val maxY = (size.height * (zoom - 1) / 2f)
    
            offset = Offset(
                newOffset.x.coerceIn(-maxX, maxX),
                newOffset.y.coerceIn(-maxY, maxY)
            )
        }
    )
    }
    

    https://stackoverflow.com/a/72856350/5457853

    And for natural pan and zooming which happens center of fingers instead of center of screen you can refer this official code

    https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.ui.input.pointer.PointerInputScope).detectTransformGestures(kotlin.Boolean,kotlin.Function4)

    You can also consider checking this zoom library which offers features i mentioned above and more out of the box.

    https://github.com/SmartToolFactory/Compose-Zoom