I'm trying to implement a feature in my Android app where users can pan and zoom images within an ImageView that has its scaleType set to centerCrop and loading image by Glide into ImageView. The goal is to allow users to view the entire image, even though it may be larger than the ImageView itself. When the user lifts their finger, the image should smoothly snap back to its original position.
I've provided the XML code for the ImageView and the Java code for handling touch events, but the image is not moving or zooming. Can anyone help me fix the code so that the user can move image inside imageview to view complete center-crop image?
XML Code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
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:fillViewport="true"
android:clickable="true"
android:focusable="true"
android:layout_height="match_parent"
android:layout_width="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/newPost_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:clickable="true"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<ImageView
android:id="@+id/newPost_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:minHeight="300dp"
android:scaleType="centerCrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/newPost_statusText"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
Java Code:
preview.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
// Switch based on the touch event action
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: // When finger is pressed down
// Save the current image transformation matrix
matrix.set(preview.getImageMatrix());
// Record the touch position for later calculation
lastTouchX = event.getX();
lastTouchY = event.getY();
// Set the active pointer ID for tracking subsequent moves
activePointerId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE: // When finger is moving
// Find the index of the active pointer
int pointerIndex = event.findPointerIndex(activePointerId);
// Check if the active pointer is still valid
if (pointerIndex != -1) {
// Calculate the difference between the current touch position and the previous touch position
float dx = event.getX(pointerIndex) - lastTouchX;
float dy = event.getY(pointerIndex) - lastTouchY;
// Apply the translation to the image transformation matrix
matrix.postTranslate(dx, dy);
// Update the image view with the transformed matrix
preview.setImageMatrix(matrix);
// Update the last touch position for the next move
lastTouchX = event.getX(pointerIndex);
lastTouchY = event.getY(pointerIndex);
}
break;
case MotionEvent.ACTION_UP: // When finger is lifted up
// Reset the active pointer ID
activePointerId = INVALID_POINTER_ID;
// Apply any remaining translation to the image view
preview.setImageMatrix(matrix);
break;
case MotionEvent.ACTION_POINTER_UP: // When a second finger is lifted up
// Find the index of the pointer that was lifted up
int pointerIndexUP = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndexUP);
// Check if the lifted pointer is the active pointer
if (pointerId == activePointerId) {
// Update the active pointer ID to the remaining finger
int newPointerIndex = pointerIndexUP == 0 ? 1 : 0;
lastTouchX = event.getX(newPointerIndex);
lastTouchY = event.getY(newPointerIndex);
activePointerId = event.getPointerId(newPointerIndex);
}
break;
}
return true;
}
});