kotlinimageviewpositionpanzoom-sdk

How to get the position of the image in a imageview after the scaleX and scaleY are changed in Kotlin


I need to get the position, width and height of image inside ImageView after the scale has changed. I need the values to add code to control the scrolling limits. I has seen other threads that use a Matrix mechanism but in my case they did not apply because my image does goes beyond the ImageView on purpose since the scale change is to zoom image 2 and 3 times.

What I need is to keep the image not to show empty space when scrolling. For instance the top left corner of the image can't go below the top border of the view nor go far right of the left border of the view. Similar on the other corners, the image can't go over the bottom border nor far left of the right border, etc.

When I do pdfview.x and pdfview.width etc. the values are giving me the original value. I could get the width and height by math but can't do it with the position as it will change when scrolling.

I tried getting the bounds but it also gives me the original values.

Bounds code:

var bounds: RectF = getImageBounds(pdfview)

fun getImageBounds(imageView: ImageView): RectF {
    val bounds = RectF()
    val drawable = imageView.drawable
    if (drawable != null) {
        imageView.imageMatrix.mapRect(bounds, RectF(drawable.bounds))
    }
    return bounds
}

Zoom code:

val binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
        
pdfview = binding.pdfview
    slider = binding.slider

    slider.addOnChangeListener { slider, value, fromUser ->
        // Responds to when slider's value is changed
        pdfview.scaleX = 1 + slider.value / 100
        pdfview.scaleY = 1 + slider.value / 100
    }

PAN code:

var preX: Float = pdfview.x
    var preY: Float = pdfview.y
    
    pdfview.setOnTouchListener { v, event ->
        val currentX: Float
        val currentY: Float

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                preX = event.x
                preY = event.y
            }

            MotionEvent.ACTION_MOVE -> {
                currentX = event.x
                currentY = event.y
                    pdfview.scrollBy((preX - currentX).toInt(), (preY - currentY).toInt())
                    preX = currentX
                    preY = currentY
            }

            MotionEvent.ACTION_UP -> {
                currentX = event.x
                currentY = event.y
                    pdfview.scrollBy((preX - currentX).toInt(), (preY - currentY).toInt())
            }
        }
        true
    }

Layout xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/myLayout"
    tools:context=".MainActivity">

<FrameLayout
        android:id="@+id/frame"
        android:layout_width="350dp"
        android:layout_height="515dp"
        android:layout_marginStart="30dp"
        android:layout_marginTop="4dp"
        android:layout_marginEnd="30dp"
        android:background="@drawable/image_border"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/creatorButton">

        <View
            android:id="@+id/view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:cropToPadding="true"
            android:padding="1dp"
            android:scaleType="centerCrop" />

        <ImageView
            android:id="@+id/pdfview"
            android:layout_width="350dp"
            android:layout_height="515dp"
            android:contentDescription="@string/pdf"
            android:cropToPadding="true"
            android:padding="1dp"
            android:scaleType="centerCrop"
            app:srcCompat="@color/material_dynamic_neutral80" />

    </FrameLayout>

<LinearLayout
        android:id="@+id/linear"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/frame"
        app:layout_constraintVertical_bias="0.0">

        <com.google.android.material.slider.Slider
            android:id="@+id/slider"
            android:layout_width="350dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="0dp"
            android:layout_marginBottom="0dp"
            android:valueFrom="0.0"
            android:valueTo="300.0" />

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Solution

  • Turns out that I found how to use the Matrix movement mechanism instead of the the scroll mechanism. The key is to do the correct Matrix redraw code.

    The following is the movement code:

    pdfview.setOnTouchListener { _, event -> //v, event ->
            val bounds: RectF = getImageBounds(pdfview, m)
    
            val currentX: Float
            val currentY: Float
    
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    preX = event.x
                    preY = event.y
                }
    
                MotionEvent.ACTION_MOVE -> {
                    currentX = event.x
                    currentY = event.y
                    //// The function xyComp is used to control the limits of the Matrix movements 
                    //// with the help of the Drawable boundaries and corner coordinates, and total size of the View. 
                    //// Those values are obtain on each movement below.
                    if (xyComp(
                            bounds,
                            fixbounds,
                            preX,
                            preY,
                            currentX,
                            currentY,
                            (preX - currentX),
                            (preY - currentY)
                        )
                    ) {
                        //// Matrix movement. postTranslate move the Matrix in continuos mode
                        m.postTranslate(
                            -(preX - currentX),
                            -(preY - currentY)
                        )
    
                        //// Redraw Matrix by assigning the moved Matrix to the View, do not use invalidate method 
                        pdfview.imageMatrix = m
    
                        //// Get the size(top,left,bottom,right) coordinates of the Drawable/Matrix within the View
                        pbounds = getImageBounds(pdfview)
                        
                        //// Get the Matrix corner coordinates
                        val f = FloatArray(9)
                        m.getValues(f)
                        pscrollX = f[Matrix.MTRANS_X]
                        pscrollY = f[Matrix.MTRANS_Y]
    
                        //// With the size and corner then you can control the limits which can it move
                        preX = currentX
                        preY = currentY
                    }
                }
    
                MotionEvent.ACTION_UP -> {
                    currentX = event.x
                    currentY = event.y
                    if (xyComp(
                            bounds,
                            fixbounds,
                            preX,
                            preY,
                            currentX,
                            currentY,
                            (preX - currentX),
                            (preY - currentY)
                        )
                    ) {
                        m.postTranslate(
                            -(preX - currentX),
                            -(preY - currentY)
                        )
                        pdfview.imageMatrix = m
                        pbounds = getImageBounds(pdfview)
                        val f = FloatArray(9)
                        m.getValues(f)
    
                        pscrollX = f[Matrix.MTRANS_X]
                        pscrollY = f[Matrix.MTRANS_Y]
    
                    }
                }
            }
            if (pscrollX != scrollX) {
                switchPAN.tag = true
                switchPAN.visibility = VISIBLE
                switchCenter.visibility = GONE
            }
            true
        }