i have been encoutering errors in getting acurate values of the area that needs to be cropped.the cropped image is usually too much to the left and bottom. .It even shows negative width and height if you zoom too much.
example of when if(left \>= 0 && top \>= 0 && width \> 0 && height \> 0)
is false:
left = 2303.7014
top = 1823.5114
right = 1080.0
bottom = 1080.0
width = -1223
height = -743
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
public class CropImageActivity extends AppCompatActivity {
private ImageView imageView;
private ScaleGestureDetector scaleGestureDetector;
private Matrix matrix = new Matrix();
private TextView saveButton;
private float scaleFactor = 1.0f;
private Uri imageUri;
private float lastX, lastY;
private String TAG ="CropImage Activity";
private ActivityResultLauncher<Intent> selectImageLauncher;
private TextView backButton;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crop_image);
imageView = findViewById(R.id.crop_image_image_view);
saveButton = findViewById(R.id.crop_image_save_button);
backButton = findViewById(R.id.crop_image_select_button);
scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
imageView.setScaleType(ImageView.ScaleType.MATRIX);
selectImageLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result ->{
try {
assert result.getData() != null;
Log.d(TAG, result.toString());
imageUri = result.getData().getData();
Picasso.get().load(imageUri).into(imageView);
}catch (Exception e){
Log.d(TAG, Objects.requireNonNull(e.getMessage()));
}
});
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
selectImageLauncher.launch(intent);
backButton.setOnClickListener(view->{
Intent intentAgaim = new Intent(Intent.ACTION_GET_CONTENT);
intentAgaim.setType("image/*");
selectImageLauncher.launch(intentAgaim);
});
imageView.setOnTouchListener((v, event) -> {
scaleGestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getX();
lastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float deltaX = event.getX() - lastX;
float deltaY = event.getY() - lastY;
// Get the current matrix of the ImageView
matrix.set(imageView.getImageMatrix());
// Translate the matrix by the change in X and Y
Log.d(TAG, "deltX = "+deltaX+" deltaY = "+deltaY);
matrix.postTranslate(deltaX, deltaY);
// Apply the new matrix to the ImageView
imageView.setImageMatrix(matrix);
imageView.invalidate();
// Update the last touch coordinates
lastX = event.getX();
lastY = event.getY();
break;
}
return true;
});
saveButton.setOnClickListener(view ->{
if (cropImage() == null){
Toast.makeText(this, "cropping failed", Toast.LENGTH_SHORT).show();
}else {
Intent resultIntent = new Intent();
resultIntent.putExtra("cropped_image", cropImage());
setResult(RESULT_OK, resultIntent);
finish();
}
});
}
private Uri cropImage() {
RectF visibleRect = new RectF();
Matrix imageMatrix = imageView.getImageMatrix();
imageMatrix.mapRect(visibleRect);
Bitmap croppedBitmap;
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
int imageWidth = drawable.getIntrinsicWidth();
int imageHeight = drawable.getIntrinsicHeight();
float[] matrixValues = new float[9];
imageMatrix.getValues(matrixValues);
float imageX = matrixValues[Matrix.MTRANS_X];
float imageY = matrixValues[Matrix.MTRANS_Y];
// Calculate the adjusted cropping area
float left = Math.max(0, -visibleRect.left / matrixValues[Matrix.MSCALE_X] - imageX);
float top = Math.max(0, -visibleRect.top / matrixValues[Matrix.MSCALE_Y] - imageY);
float right = Math.min(imageWidth, (imageWidth - visibleRect.left) / matrixValues[Matrix.MSCALE_X] - imageX);
float bottom = Math.min(imageHeight, (imageHeight - visibleRect.top) / matrixValues[Matrix.MSCALE_Y] - imageY);
int width = (int) (right - left);
int height = (int) (bottom - top);
Bitmap sourceBitmap = ((BitmapDrawable) drawable).getBitmap();
if (left >= 0 && top >= 0 && width > 0 && height > 0) {
try {
// Create a cropped bitmap from the source bitmap using the calculated area
croppedBitmap = Bitmap.createBitmap(sourceBitmap, (int) left, (int) top, width, height);
// Check if the croppedBitmap is not null before attempting to compress it
if (croppedBitmap != null) {
// Create a temporary file to save the cropped bitmap
File tempFile = createTempImageFile(this);
if (tempFile != null) {
try {
// Write the cropped bitmap to the file
FileOutputStream fos = new FileOutputStream(tempFile);
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
// Create a Uri from the temporary file
Uri croppedImageUri = Uri.fromFile(tempFile);
return croppedImageUri;
} catch (IOException e) {
e.printStackTrace();
return null;
}
} else {
Log.d(TAG, "croppedBitmap is = null");
return null;
}
} else {
Log.d(TAG, "croppedBitmap is = null");
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
Log.d(TAG, "if(left >= 0 && top >= 0 && width > 0 && height > 0) failed");
Log.d(TAG, "left = " + left);
Log.d(TAG, "top = " + top);
Log.d(TAG, "right = " + right);
Log.d(TAG, "bottom = " + bottom);
Log.d(TAG, "width = " + width);
Log.d(TAG, "height = " + height);
Toast.makeText(this, "Invalid cropping area", Toast.LENGTH_SHORT).show();
return null;
}
} else {
Log.d(TAG, "drawable equals null");
return null;
}
}
public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private float scaleFactor = 1.0f;
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(1.0f, Math.min(scaleFactor, 3.0f));
// Adjust the matrix to apply zoom
matrix.setScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
imageView.setImageMatrix(matrix);
return true;
}
}
private File createTempImageFile(Context context) {
try {
// Create a temporary file in the app's cache directory
File cacheDir = context.getCacheDir();
File tempFile = File.createTempFile("temp_image", ".png", cacheDir);
return tempFile;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
// Calculate the adjusted cropping area within the image boundaries float left = Math.max(0, visibleRect.left / scaleFactor); float top = Math.max(0, visibleRect.top / scaleFactor); float right = Math.min(imageWidth, (imageWidth - visibleRect.left) / scaleFactor); float bottom = Math.min(imageHeight, (imageHeight - visibleRect.top) / scaleFactor);
This resulted in i same values, so width and height became zero.