This post describes how to crop and rotate image.
Create your layout in activity_main.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"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PICK"
android:layout_margin="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button1"
tools:srcCompat="@tools:sample/avatars" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ROTATE"
android:layout_margin="4dp"
app:layout_constraintStart_toEndOf="@id/button1"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CROP"
android:layout_margin="4dp"
app:layout_constraintStart_toEndOf="@id/button2"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>In MainActivity.java use following codes.
package com.example.mathfragments;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MainActivity extends AppCompatActivity {
ImageView imageview1;
String image_uri = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = findViewById(R.id.button1);
Button button2 = findViewById(R.id.button2);
Button button3 = findViewById(R.id.button3);
imageview1 = findViewById(R.id.imageView1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mGetContent.launch("image/*");
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Bitmap showBitmap = getBitmapFromCache();
Bitmap rotatedImage = rotateBitmap(showBitmap);
saveBitmapToCache(rotatedImage);
imageview1.setImageBitmap(getBitmapFromCache());
} catch (IOException e){
Log.e("tag", e.toString());
}
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, CropActivity.class);
startActivity(intent);
}
});
}
ActivityResultLauncher<String> mGetContent = registerForActivityResult(new ActivityResultContracts.GetContent(),
new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri uri) {
image_uri = uri.toString();
try {
Bitmap showBitmap = getBitmapFromUri(uri);
saveBitmapToCache(showBitmap);
imageview1.setImageBitmap(showBitmap);
} catch (IOException e){
Log.e("tag", e.toString());
}
}
});
@Override
protected void onStart() {
super.onStart();
if (!image_uri.equals("")){
imageview1.setImageBitmap(getBitmapFromCache());
}
}
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return image;
}
public void saveBitmapToCache(Bitmap bitmap) throws IOException {
String filename = "final_image.jpg";
File cacheFile = new File(getApplicationContext().getCacheDir(), filename);
OutputStream out = new FileOutputStream(cacheFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, (int)100, out);
out.flush();
out.close();
}
public Bitmap getBitmapFromCache(){
File cacheFile = new File(getApplicationContext().getCacheDir(), "final_image.jpg");
Bitmap myBitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath());
return myBitmap;
}
public Bitmap rotateBitmap(Bitmap bitmap){
android.graphics.Matrix matrix = new android.graphics.Matrix();
matrix.postScale((float)1, (float)1);
matrix.postRotate(90);
Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
return bitmap2;
}
}Create new java file CropUtils.java.
package com.example.mathfragments;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
public class CropUtils {
public static class CropImageView extends androidx.appcompat.widget.AppCompatImageView {
@SuppressWarnings("unused")
private static final String TAG = CropImageView.class.getName();
@SuppressWarnings("unused")
public static final int GUIDELINES_OFF = 0;
public static final int GUIDELINES_ON_TOUCH = 1;
public static final int GUIDELINES_ON = 2;
private Paint mBorderPaint;
private Paint mGuidelinePaint;
private Paint mCornerPaint;
private Paint mSurroundingAreaOverlayPaint;
private float mHandleRadius;
private float mSnapRadius;
private float mCornerThickness;
private float mBorderThickness;
private float mCornerLength;
private RectF mBitmapRect = new RectF();
private final PointF mTouchOffset = new PointF();
private Handle mPressedHandle;
private boolean mFixAspectRatio;
private int mAspectRatioX = 1;
private int mAspectRatioY = 1;
private int mGuidelinesMode = 1;
public CropImageView(Context context) {
super(context);
init(context, null);
}
public CropImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CropImageView, 0, 0);
mGuidelinesMode = 1;
mFixAspectRatio = false;
mAspectRatioX = 1;
mAspectRatioY = 1;
final android.content.res.Resources resources = context.getResources();
mBorderPaint = PaintUtil.newBorderPaint(resources);
mGuidelinePaint = PaintUtil.newGuidelinePaint(resources);
mSurroundingAreaOverlayPaint = PaintUtil.newSurroundingAreaOverlayPaint(resources);
mCornerPaint = PaintUtil.newCornerPaint(resources);
mHandleRadius = 24;
mSnapRadius = 3;
mBorderThickness = 3;
mCornerThickness = 5;
mCornerLength = 20;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mBitmapRect = getBitmapRect();
initCropWindow(mBitmapRect);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawDarkenedSurroundingArea(canvas);
drawGuidelines(canvas);
drawBorder(canvas);
drawCorners(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onActionDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
onActionUp();
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return true;
default:
return false;
}
}
public void setGuidelines(int guidelinesMode) {
mGuidelinesMode = guidelinesMode;
invalidate();
}
public void setFixedAspectRatio(boolean fixAspectRatio) {
mFixAspectRatio = fixAspectRatio;
requestLayout();
}
public void setAspectRatio(int aspectRatioX, int aspectRatioY) {
if (aspectRatioX <= 0 || aspectRatioY <= 0) {
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
}
mAspectRatioX = aspectRatioX;
mAspectRatioY = aspectRatioY;
if (mFixAspectRatio) {
requestLayout();
}
}
public Bitmap getCroppedImage() {
final android.graphics.drawable.Drawable drawable = getDrawable();
if (drawable == null || !(drawable instanceof android.graphics.drawable.BitmapDrawable)) {
return null;
}
final float[] matrixValues = new float[9];
getImageMatrix().getValues(matrixValues);
final float scaleX = matrixValues[Matrix.MSCALE_X];
final float scaleY = matrixValues[Matrix.MSCALE_Y];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
final float bitmapLeft = (transX < 0) ? Math.abs(transX) : 0;
final float bitmapTop = (transY < 0) ? Math.abs(transY) : 0;
final Bitmap originalBitmap = ((android.graphics.drawable.BitmapDrawable) drawable).getBitmap();
final float cropX = (bitmapLeft + Edge.LEFT.getCoordinate()) / scaleX;
final float cropY = (bitmapTop + Edge.TOP.getCoordinate()) / scaleY;
final float cropWidth = Math.min(Edge.getWidth() / scaleX, originalBitmap.getWidth() - cropX);
final float cropHeight = Math.min(Edge.getHeight() / scaleY, originalBitmap.getHeight() - cropY);
return Bitmap.createBitmap(originalBitmap,
(int) cropX,
(int) cropY,
(int) cropWidth,
(int) cropHeight);
}
private RectF getBitmapRect() {
final android.graphics.drawable.Drawable drawable = getDrawable();
if (drawable == null) {
return new RectF();
}
final float[] matrixValues = new float[9];
getImageMatrix().getValues(matrixValues);
final float scaleX = matrixValues[Matrix.MSCALE_X];
final float scaleY = matrixValues[Matrix.MSCALE_Y];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
final int drawableIntrinsicWidth = drawable.getIntrinsicWidth();
final int drawableIntrinsicHeight = drawable.getIntrinsicHeight();
final int drawableDisplayWidth = Math.round(drawableIntrinsicWidth * scaleX);
final int drawableDisplayHeight = Math.round(drawableIntrinsicHeight * scaleY);
final float left = Math.max(transX, 0);
final float top = Math.max(transY, 0);
final float right = Math.min(left + drawableDisplayWidth, getWidth());
final float bottom = Math.min(top + drawableDisplayHeight, getHeight());
return new RectF(left, top, right, bottom);
}
private void initCropWindow(RectF bitmapRect) {
if (mFixAspectRatio) {
initCropWindowWithFixedAspectRatio(bitmapRect);
} else {
final float horizontalPadding = 0.1f * bitmapRect.width();
final float verticalPadding = 0.1f * bitmapRect.height();
Edge.LEFT.setCoordinate(bitmapRect.left + horizontalPadding);
Edge.TOP.setCoordinate(bitmapRect.top + verticalPadding);
Edge.RIGHT.setCoordinate(bitmapRect.right - horizontalPadding);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom - verticalPadding);
}
}
private void initCropWindowWithFixedAspectRatio(RectF bitmapRect) {
if (AspectRatioUtil.calculateAspectRatio(bitmapRect) > getTargetAspectRatio()) {
final float cropWidth = AspectRatioUtil.calculateWidth(bitmapRect.height(), getTargetAspectRatio());
Edge.LEFT.setCoordinate(bitmapRect.centerX() - cropWidth / 2f);
Edge.TOP.setCoordinate(bitmapRect.top);
Edge.RIGHT.setCoordinate(bitmapRect.centerX() + cropWidth / 2f);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom);
} else {
final float cropHeight = AspectRatioUtil.calculateHeight(bitmapRect.width(), getTargetAspectRatio());
Edge.LEFT.setCoordinate(bitmapRect.left);
Edge.TOP.setCoordinate(bitmapRect.centerY() - cropHeight / 2f);
Edge.RIGHT.setCoordinate(bitmapRect.right);
Edge.BOTTOM.setCoordinate(bitmapRect.centerY() + cropHeight / 2f);
}
}
private void drawDarkenedSurroundingArea(Canvas canvas) {
final RectF bitmapRect = mBitmapRect;
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, top, mSurroundingAreaOverlayPaint);
canvas.drawRect(bitmapRect.left, bottom, bitmapRect.right, bitmapRect.bottom, mSurroundingAreaOverlayPaint);
canvas.drawRect(bitmapRect.left, top, left, bottom, mSurroundingAreaOverlayPaint);
canvas.drawRect(right, top, bitmapRect.right, bottom, mSurroundingAreaOverlayPaint);
}
private void drawGuidelines(Canvas canvas) {
if (!shouldGuidelinesBeShown()) {
return;
}
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
final float oneThirdCropWidth = Edge.getWidth() / 3;
final float x1 = left + oneThirdCropWidth;
canvas.drawLine(x1, top, x1, bottom, mGuidelinePaint);
final float x2 = right - oneThirdCropWidth;
canvas.drawLine(x2, top, x2, bottom, mGuidelinePaint);
final float oneThirdCropHeight = Edge.getHeight() / 3;
final float y1 = top + oneThirdCropHeight;
canvas.drawLine(left, y1, right, y1, mGuidelinePaint);
final float y2 = bottom - oneThirdCropHeight;
canvas.drawLine(left, y2, right, y2, mGuidelinePaint);
}
private void drawBorder(Canvas canvas) {
canvas.drawRect(Edge.LEFT.getCoordinate(),
Edge.TOP.getCoordinate(),
Edge.RIGHT.getCoordinate(),
Edge.BOTTOM.getCoordinate(),
mBorderPaint);
}
private void drawCorners(Canvas canvas) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
final float lateralOffset = (mCornerThickness - mBorderThickness) / 2f;
final float startOffset = mCornerThickness - (mBorderThickness / 2f);
canvas.drawLine(left - lateralOffset, top - startOffset, left - lateralOffset, top + mCornerLength, mCornerPaint);
canvas.drawLine(left - startOffset, top - lateralOffset, left + mCornerLength, top - lateralOffset, mCornerPaint);
canvas.drawLine(right + lateralOffset, top - startOffset, right + lateralOffset, top + mCornerLength, mCornerPaint);
canvas.drawLine(right + startOffset, top - lateralOffset, right - mCornerLength, top - lateralOffset, mCornerPaint);
canvas.drawLine(left - lateralOffset, bottom + startOffset, left - lateralOffset, bottom - mCornerLength, mCornerPaint);
canvas.drawLine(left - startOffset, bottom + lateralOffset, left + mCornerLength, bottom + lateralOffset, mCornerPaint);
canvas.drawLine(right + lateralOffset, bottom + startOffset, right + lateralOffset, bottom - mCornerLength, mCornerPaint);
canvas.drawLine(right + startOffset, bottom + lateralOffset, right - mCornerLength, bottom + lateralOffset, mCornerPaint);
}
private boolean shouldGuidelinesBeShown() {
return ((mGuidelinesMode == GUIDELINES_ON)
|| ((mGuidelinesMode == GUIDELINES_ON_TOUCH) && (mPressedHandle != null)));
}
private float getTargetAspectRatio() {
return mAspectRatioX / (float) mAspectRatioY;
}
private void onActionDown(float x, float y) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);
if (mPressedHandle != null) {
HandleUtil.getOffset(mPressedHandle, x, y, left, top, right, bottom, mTouchOffset);
invalidate();
}
}
private void onActionUp() {
if (mPressedHandle != null) {
mPressedHandle = null;
invalidate();
}
}
private void onActionMove(float x, float y) {
if (mPressedHandle == null) {
return;
}
x += mTouchOffset.x;
y += mTouchOffset.y;
if (mFixAspectRatio) {
mPressedHandle.updateCropWindow(x, y, getTargetAspectRatio(), mBitmapRect, mSnapRadius);
} else {
mPressedHandle.updateCropWindow(x, y, mBitmapRect, mSnapRadius);
}
invalidate();
}
}
public static class PaintUtil {
public static Paint newBorderPaint(android.content.res.Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.parseColor("#AAFFFFFF"));
return paint;
}
public static Paint newGuidelinePaint(android.content.res.Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(Color.parseColor("#AAFFFFFF"));
return paint;
}
public static Paint newSurroundingAreaOverlayPaint(android.content.res.Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#B0000000"));
return paint;
}
public static Paint newCornerPaint(android.content.res.Resources resources) {
final Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.parseColor("#FFFFFF"));
return paint;
}
}
public static class MathUtil {
public static float calculateDistance(float x1, float y1, float x2, float y2) {
final float side1 = x2 - x1;
final float side2 = y2 - y1;
return (float) Math.sqrt(side1 * side1 + side2 * side2);
}
}
public static class HandleUtil {
public static Handle getPressedHandle(float x,
float y,
float left,
float top,
float right,
float bottom,
float targetRadius) {
Handle closestHandle = null;
float closestDistance = Float.POSITIVE_INFINITY;
final float distanceToTopLeft = MathUtil.calculateDistance(x, y, left, top);
if (distanceToTopLeft < closestDistance) {
closestDistance = distanceToTopLeft;
closestHandle = Handle.TOP_LEFT;
}
final float distanceToTopRight = MathUtil.calculateDistance(x, y, right, top);
if (distanceToTopRight < closestDistance) {
closestDistance = distanceToTopRight;
closestHandle = Handle.TOP_RIGHT;
}
final float distanceToBottomLeft = MathUtil.calculateDistance(x, y, left, bottom);
if (distanceToBottomLeft < closestDistance) {
closestDistance = distanceToBottomLeft;
closestHandle = Handle.BOTTOM_LEFT;
}
final float distanceToBottomRight = MathUtil.calculateDistance(x, y, right, bottom);
if (distanceToBottomRight < closestDistance) {
closestDistance = distanceToBottomRight;
closestHandle = Handle.BOTTOM_RIGHT;
}
if (closestDistance <= targetRadius) {
return closestHandle;
}
if (HandleUtil.isInHorizontalTargetZone(x, y, left, right, top, targetRadius)) {
return Handle.TOP;
} else if (HandleUtil.isInHorizontalTargetZone(x, y, left, right, bottom, targetRadius)) {
return Handle.BOTTOM;
} else if (HandleUtil.isInVerticalTargetZone(x, y, left, top, bottom, targetRadius)) {
return Handle.LEFT;
} else if (HandleUtil.isInVerticalTargetZone(x, y, right, top, bottom, targetRadius)) {
return Handle.RIGHT;
}
if (isWithinBounds(x, y, left, top, right, bottom)) {
return Handle.CENTER;
}
return null;
}
public static void getOffset(Handle handle,
float x,
float y,
float left,
float top,
float right,
float bottom,
PointF touchOffsetOutput) {
float touchOffsetX = 0;
float touchOffsetY = 0;
switch (handle) {
case TOP_LEFT:
touchOffsetX = left - x;
touchOffsetY = top - y;
break;
case TOP_RIGHT:
touchOffsetX = right - x;
touchOffsetY = top - y;
break;
case BOTTOM_LEFT:
touchOffsetX = left - x;
touchOffsetY = bottom - y;
break;
case BOTTOM_RIGHT:
touchOffsetX = right - x;
touchOffsetY = bottom - y;
break;
case LEFT:
touchOffsetX = left - x;
touchOffsetY = 0;
break;
case TOP:
touchOffsetX = 0;
touchOffsetY = top - y;
break;
case RIGHT:
touchOffsetX = right - x;
touchOffsetY = 0;
break;
case BOTTOM:
touchOffsetX = 0;
touchOffsetY = bottom - y;
break;
case CENTER:
final float centerX = (right + left) / 2;
final float centerY = (top + bottom) / 2;
touchOffsetX = centerX - x;
touchOffsetY = centerY - y;
break;
}
touchOffsetOutput.x = touchOffsetX;
touchOffsetOutput.y = touchOffsetY;
}
private static boolean isInHorizontalTargetZone(float x,
float y,
float handleXStart,
float handleXEnd,
float handleY,
float targetRadius) {
return (x > handleXStart && x < handleXEnd && Math.abs(y - handleY) <= targetRadius);
}
private static boolean isInVerticalTargetZone(float x,
float y,
float handleX,
float handleYStart,
float handleYEnd,
float targetRadius) {
return (Math.abs(x - handleX) <= targetRadius && y > handleYStart && y < handleYEnd);
}
private static boolean isWithinBounds(float x, float y, float left, float top, float right, float bottom) {
return x >= left && x <= right && y >= top && y <= bottom;
}
}
public static class AspectRatioUtil {
public static float calculateAspectRatio(float left, float top, float right, float bottom) {
final float width = right - left;
final float height = bottom - top;
return width / height;
}
public static float calculateAspectRatio(RectF rect) {
return rect.width() / rect.height();
}
public static float calculateLeft(float top, float right, float bottom, float targetAspectRatio) {
final float height = bottom - top;
return right - (targetAspectRatio * height);
}
public static float calculateTop(float left, float right, float bottom, float targetAspectRatio) {
final float width = right - left;
return bottom - (width / targetAspectRatio);
}
public static float calculateRight(float left, float top, float bottom, float targetAspectRatio) {
final float height = bottom - top;
return (targetAspectRatio * height) + left;
}
public static float calculateBottom(float left, float top, float right, float targetAspectRatio) {
final float width = right - left;
return (width / targetAspectRatio) + top;
}
public static float calculateWidth(float height, float targetAspectRatio) {
return targetAspectRatio * height;
}
public static float calculateHeight(float width, float targetAspectRatio) {
return width / targetAspectRatio;
}
}
public enum Handle {
TOP_LEFT(new CornerHandleHelper(Edge.TOP, Edge.LEFT)),
TOP_RIGHT(new CornerHandleHelper(Edge.TOP, Edge.RIGHT)),
BOTTOM_LEFT(new CornerHandleHelper(Edge.BOTTOM, Edge.LEFT)),
BOTTOM_RIGHT(new CornerHandleHelper(Edge.BOTTOM, Edge.RIGHT)),
LEFT(new VerticalHandleHelper(Edge.LEFT)),
TOP(new HorizontalHandleHelper(Edge.TOP)),
RIGHT(new VerticalHandleHelper(Edge.RIGHT)),
BOTTOM(new HorizontalHandleHelper(Edge.BOTTOM)),
CENTER(new CenterHandleHelper());
private final HandleHelper mHelper;
Handle(HandleHelper helper) {
mHelper = helper;
}
public void updateCropWindow(float x,
float y,
RectF imageRect,
float snapRadius) {
mHelper.updateCropWindow(x, y, imageRect, snapRadius);
}
public void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius) {
mHelper.updateCropWindow(x, y, targetAspectRatio, imageRect, snapRadius);
}
}
static abstract class HandleHelper {
private static final float UNFIXED_ASPECT_RATIO_CONSTANT = 1;
private final Edge mHorizontalEdge;
private final Edge mVerticalEdge;
private final EdgePair mActiveEdges;
HandleHelper(Edge horizontalEdge, Edge verticalEdge) {
mHorizontalEdge = horizontalEdge;
mVerticalEdge = verticalEdge;
mActiveEdges = new EdgePair(mHorizontalEdge, mVerticalEdge);
}
void updateCropWindow(float x,
float y,
RectF imageRect,
float snapRadius) {
final EdgePair activeEdges = getActiveEdges();
final Edge primaryEdge = activeEdges.primary;
final Edge secondaryEdge = activeEdges.secondary;
if (primaryEdge != null)
primaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, UNFIXED_ASPECT_RATIO_CONSTANT);
if (secondaryEdge != null)
secondaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, UNFIXED_ASPECT_RATIO_CONSTANT);
}
abstract void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius);
EdgePair getActiveEdges() {
return mActiveEdges;
}
EdgePair getActiveEdges(float x, float y, float targetAspectRatio) {
final float potentialAspectRatio = getAspectRatio(x, y);
if (potentialAspectRatio > targetAspectRatio) {
mActiveEdges.primary = mVerticalEdge;
mActiveEdges.secondary = mHorizontalEdge;
} else {
mActiveEdges.primary = mHorizontalEdge;
mActiveEdges.secondary = mVerticalEdge;
}
return mActiveEdges;
}
private float getAspectRatio(float x, float y) {
final float left = (mVerticalEdge == Edge.LEFT) ? x : Edge.LEFT.getCoordinate();
final float top = (mHorizontalEdge == Edge.TOP) ? y : Edge.TOP.getCoordinate();
final float right = (mVerticalEdge == Edge.RIGHT) ? x : Edge.RIGHT.getCoordinate();
final float bottom = (mHorizontalEdge == Edge.BOTTOM) ? y : Edge.BOTTOM.getCoordinate();
return AspectRatioUtil.calculateAspectRatio(left, top, right, bottom);
}
}
static class HorizontalHandleHelper extends HandleHelper {
private Edge mEdge;
HorizontalHandleHelper(Edge edge) {
super(edge, null);
mEdge = edge;
}
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius) {
mEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
float left = Edge.LEFT.getCoordinate();
float right = Edge.RIGHT.getCoordinate();
final float targetWidth = AspectRatioUtil.calculateWidth(Edge.getHeight(), targetAspectRatio);
final float difference = targetWidth - Edge.getWidth();
final float halfDifference = difference / 2;
left -= halfDifference;
right += halfDifference;
Edge.LEFT.setCoordinate(left);
Edge.RIGHT.setCoordinate(right);
if (Edge.LEFT.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.LEFT, imageRect, targetAspectRatio)) {
final float offset = Edge.LEFT.snapToRect(imageRect);
Edge.RIGHT.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
if (Edge.RIGHT.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.RIGHT, imageRect, targetAspectRatio)) {
final float offset = Edge.RIGHT.snapToRect(imageRect);
Edge.LEFT.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
}
}
static class VerticalHandleHelper extends HandleHelper {
private Edge mEdge;
VerticalHandleHelper(Edge edge) {
super(null, edge);
mEdge = edge;
}
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius) {
mEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
float top = Edge.TOP.getCoordinate();
float bottom = Edge.BOTTOM.getCoordinate();
final float targetHeight = AspectRatioUtil.calculateHeight(Edge.getWidth(), targetAspectRatio);
final float difference = targetHeight - Edge.getHeight();
final float halfDifference = difference / 2;
top -= halfDifference;
bottom += halfDifference;
Edge.TOP.setCoordinate(top);
Edge.BOTTOM.setCoordinate(bottom);
if (Edge.TOP.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.TOP, imageRect, targetAspectRatio)) {
final float offset = Edge.TOP.snapToRect(imageRect);
Edge.BOTTOM.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
if (Edge.BOTTOM.isOutsideMargin(imageRect, snapRadius)
&& !mEdge.isNewRectangleOutOfBounds(Edge.BOTTOM, imageRect, targetAspectRatio)) {
final float offset = Edge.BOTTOM.snapToRect(imageRect);
Edge.TOP.offset(-offset);
mEdge.adjustCoordinate(targetAspectRatio);
}
}
}
static class CenterHandleHelper extends HandleHelper {
CenterHandleHelper() {
super(null, null);
}
@Override
void updateCropWindow(float x,
float y,
RectF imageRect,
float snapRadius) {
float left = Edge.LEFT.getCoordinate();
float top = Edge.TOP.getCoordinate();
float right = Edge.RIGHT.getCoordinate();
float bottom = Edge.BOTTOM.getCoordinate();
final float currentCenterX = (left + right) / 2;
final float currentCenterY = (top + bottom) / 2;
final float offsetX = x - currentCenterX;
final float offsetY = y - currentCenterY;
Edge.LEFT.offset(offsetX);
Edge.TOP.offset(offsetY);
Edge.RIGHT.offset(offsetX);
Edge.BOTTOM.offset(offsetY);
if (Edge.LEFT.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.LEFT.snapToRect(imageRect);
Edge.RIGHT.offset(offset);
} else if (Edge.RIGHT.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.RIGHT.snapToRect(imageRect);
Edge.LEFT.offset(offset);
}
if (Edge.TOP.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.TOP.snapToRect(imageRect);
Edge.BOTTOM.offset(offset);
} else if (Edge.BOTTOM.isOutsideMargin(imageRect, snapRadius)) {
final float offset = Edge.BOTTOM.snapToRect(imageRect);
Edge.TOP.offset(offset);
}
}
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius) {
updateCropWindow(x, y, imageRect, snapRadius);
}
}
static class CornerHandleHelper extends HandleHelper {
CornerHandleHelper(Edge horizontalEdge, Edge verticalEdge) {
super(horizontalEdge, verticalEdge);
}
@Override
void updateCropWindow(float x,
float y,
float targetAspectRatio,
RectF imageRect,
float snapRadius) {
final EdgePair activeEdges = getActiveEdges(x, y, targetAspectRatio);
final Edge primaryEdge = activeEdges.primary;
final Edge secondaryEdge = activeEdges.secondary;
primaryEdge.adjustCoordinate(x, y, imageRect, snapRadius, targetAspectRatio);
secondaryEdge.adjustCoordinate(targetAspectRatio);
if (secondaryEdge.isOutsideMargin(imageRect, snapRadius)) {
secondaryEdge.snapToRect(imageRect);
primaryEdge.adjustCoordinate(targetAspectRatio);
}
}
}
public enum Edge {
LEFT,
TOP,
RIGHT,
BOTTOM;
public static final int MIN_CROP_LENGTH_PX = 40;
private float mCoordinate;
public void setCoordinate(float coordinate) {
mCoordinate = coordinate;
}
public void offset(float distance) {
mCoordinate += distance;
}
public float getCoordinate() {
return mCoordinate;
}
public void adjustCoordinate(float x, float y, RectF imageRect, float imageSnapRadius, float aspectRatio) {
switch (this) {
case LEFT:
mCoordinate = adjustLeft(x, imageRect, imageSnapRadius, aspectRatio);
break;
case TOP:
mCoordinate = adjustTop(y, imageRect, imageSnapRadius, aspectRatio);
break;
case RIGHT:
mCoordinate = adjustRight(x, imageRect, imageSnapRadius, aspectRatio);
break;
case BOTTOM:
mCoordinate = adjustBottom(y, imageRect, imageSnapRadius, aspectRatio);
break;
}
}
public void adjustCoordinate(float aspectRatio) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
switch (this) {
case LEFT:
mCoordinate = AspectRatioUtil.calculateLeft(top, right, bottom, aspectRatio);
break;
case TOP:
mCoordinate = AspectRatioUtil.calculateTop(left, right, bottom, aspectRatio);
break;
case RIGHT:
mCoordinate = AspectRatioUtil.calculateRight(left, top, bottom, aspectRatio);
break;
case BOTTOM:
mCoordinate = AspectRatioUtil.calculateBottom(left, top, right, aspectRatio);
break;
}
}
public boolean isNewRectangleOutOfBounds(Edge edge, RectF imageRect, float aspectRatio) {
final float offset = edge.snapOffset(imageRect);
switch (this) {
case LEFT:
if (edge.equals(Edge.TOP)) {
final float top = imageRect.top;
final float bottom = Edge.BOTTOM.getCoordinate() - offset;
final float right = Edge.RIGHT.getCoordinate();
final float left = AspectRatioUtil.calculateLeft(top, right, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
} else if (edge.equals(Edge.BOTTOM)) {
final float bottom = imageRect.bottom;
final float top = Edge.TOP.getCoordinate() - offset;
final float right = Edge.RIGHT.getCoordinate();
final float left = AspectRatioUtil.calculateLeft(top, right, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
}
break;
case TOP:
if (edge.equals(Edge.LEFT)) {
final float left = imageRect.left;
final float right = Edge.RIGHT.getCoordinate() - offset;
final float bottom = Edge.BOTTOM.getCoordinate();
final float top = AspectRatioUtil.calculateTop(left, right, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
} else if (edge.equals(Edge.RIGHT)) {
final float right = imageRect.right;
final float left = Edge.LEFT.getCoordinate() - offset;
final float bottom = Edge.BOTTOM.getCoordinate();
final float top = AspectRatioUtil.calculateTop(left, right, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
}
break;
case RIGHT:
if (edge.equals(Edge.TOP)) {
final float top = imageRect.top;
final float bottom = Edge.BOTTOM.getCoordinate() - offset;
final float left = Edge.LEFT.getCoordinate();
final float right = AspectRatioUtil.calculateRight(left, top, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
} else if (edge.equals(Edge.BOTTOM)) {
final float bottom = imageRect.bottom;
final float top = Edge.TOP.getCoordinate() - offset;
final float left = Edge.LEFT.getCoordinate();
final float right = AspectRatioUtil.calculateRight(left, top, bottom, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
}
break;
case BOTTOM:
if (edge.equals(Edge.LEFT)) {
final float left = imageRect.left;
final float right = Edge.RIGHT.getCoordinate() - offset;
final float top = Edge.TOP.getCoordinate();
final float bottom = AspectRatioUtil.calculateBottom(left, top, right, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
} else if (edge.equals(Edge.RIGHT)) {
final float right = imageRect.right;
final float left = Edge.LEFT.getCoordinate() - offset;
final float top = Edge.TOP.getCoordinate();
final float bottom = AspectRatioUtil.calculateBottom(left, top, right, aspectRatio);
return isOutOfBounds(top, left, bottom, right, imageRect);
}
break;
}
return true;
}
private boolean isOutOfBounds(float top, float left, float bottom, float right, RectF imageRect) {
return (top < imageRect.top || left < imageRect.left || bottom > imageRect.bottom || right > imageRect.right);
}
public float snapToRect(RectF imageRect) {
final float oldCoordinate = mCoordinate;
switch (this) {
case LEFT:
mCoordinate = imageRect.left;
break;
case TOP:
mCoordinate = imageRect.top;
break;
case RIGHT:
mCoordinate = imageRect.right;
break;
case BOTTOM:
mCoordinate = imageRect.bottom;
break;
}
return mCoordinate - oldCoordinate;
}
public float snapOffset(RectF imageRect) {
final float oldCoordinate = mCoordinate;
final float newCoordinate;
switch (this) {
case LEFT:
newCoordinate = imageRect.left;
break;
case TOP:
newCoordinate = imageRect.top;
break;
case RIGHT:
newCoordinate = imageRect.right;
break;
default: // BOTTOM
newCoordinate = imageRect.bottom;
break;
}
return newCoordinate - oldCoordinate;
}
public static float getWidth() {
return Edge.RIGHT.getCoordinate() - Edge.LEFT.getCoordinate();
}
public static float getHeight() {
return Edge.BOTTOM.getCoordinate() - Edge.TOP.getCoordinate();
}
public boolean isOutsideMargin(RectF rect, float margin) {
final boolean result;
switch (this) {
case LEFT:
result = mCoordinate - rect.left < margin;
break;
case TOP:
result = mCoordinate - rect.top < margin;
break;
case RIGHT:
result = rect.right - mCoordinate < margin;
break;
default: // BOTTOM
result = rect.bottom - mCoordinate < margin;
break;
}
return result;
}
private static float adjustLeft(float x, RectF imageRect, float imageSnapRadius, float aspectRatio) {
final float resultX;
if (x - imageRect.left < imageSnapRadius) {
resultX = imageRect.left;
} else {
float resultXHoriz = Float.POSITIVE_INFINITY;
float resultXVert = Float.POSITIVE_INFINITY;
if (x >= Edge.RIGHT.getCoordinate() - MIN_CROP_LENGTH_PX) {
resultXHoriz = Edge.RIGHT.getCoordinate() - MIN_CROP_LENGTH_PX;
}
if (((Edge.RIGHT.getCoordinate() - x) / aspectRatio) <= MIN_CROP_LENGTH_PX) {
resultXVert = Edge.RIGHT.getCoordinate() - (MIN_CROP_LENGTH_PX * aspectRatio);
}
resultX = Math.min(x, Math.min(resultXHoriz, resultXVert));
}
return resultX;
}
private static float adjustRight(float x, RectF imageRect, float imageSnapRadius, float aspectRatio) {
final float resultX;
if (imageRect.right - x < imageSnapRadius) {
resultX = imageRect.right;
} else {
float resultXHoriz = Float.NEGATIVE_INFINITY;
float resultXVert = Float.NEGATIVE_INFINITY;
if (x <= Edge.LEFT.getCoordinate() + MIN_CROP_LENGTH_PX) {
resultXHoriz = Edge.LEFT.getCoordinate() + MIN_CROP_LENGTH_PX;
}
if (((x - Edge.LEFT.getCoordinate()) / aspectRatio) <= MIN_CROP_LENGTH_PX) {
resultXVert = Edge.LEFT.getCoordinate() + (MIN_CROP_LENGTH_PX * aspectRatio);
}
resultX = Math.max(x, Math.max(resultXHoriz, resultXVert));
}
return resultX;
}
private static float adjustTop(float y, RectF imageRect, float imageSnapRadius, float aspectRatio) {
final float resultY;
if (y - imageRect.top < imageSnapRadius) {
resultY = imageRect.top;
} else {
float resultYVert = Float.POSITIVE_INFINITY;
float resultYHoriz = Float.POSITIVE_INFINITY;
if (y >= Edge.BOTTOM.getCoordinate() - MIN_CROP_LENGTH_PX)
resultYHoriz = Edge.BOTTOM.getCoordinate() - MIN_CROP_LENGTH_PX;
if (((Edge.BOTTOM.getCoordinate() - y) * aspectRatio) <= MIN_CROP_LENGTH_PX)
resultYVert = Edge.BOTTOM.getCoordinate() - (MIN_CROP_LENGTH_PX / aspectRatio);
resultY = Math.min(y, Math.min(resultYHoriz, resultYVert));
}
return resultY;
}
private static float adjustBottom(float y, RectF imageRect, float imageSnapRadius, float aspectRatio) {
final float resultY;
if (imageRect.bottom - y < imageSnapRadius) {
resultY = imageRect.bottom;
} else {
float resultYVert = Float.NEGATIVE_INFINITY;
float resultYHoriz = Float.NEGATIVE_INFINITY;
if (y <= Edge.TOP.getCoordinate() + MIN_CROP_LENGTH_PX) {
resultYVert = Edge.TOP.getCoordinate() + MIN_CROP_LENGTH_PX;
}
if (((y - Edge.TOP.getCoordinate()) * aspectRatio) <= MIN_CROP_LENGTH_PX) {
resultYHoriz = Edge.TOP.getCoordinate() + (MIN_CROP_LENGTH_PX / aspectRatio);
}
resultY = Math.max(y, Math.max(resultYHoriz, resultYVert));
}
return resultY;
}
}
public static class EdgePair {
public Edge primary;
public Edge secondary;
public EdgePair(Edge edge1, Edge edge2) {
primary = edge1;
secondary = edge2;
}
}
}
Create layout file crop.xml.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<view class="com.example.mathfragments.CropUtils$CropImageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/cropimageview"
android:background="@color/purple_200"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/done_button"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/done_button"
android:layout_margin="8dp"
android:text="Done"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>In CropActivity.java use following codes.
package com.example.mathfragments;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class CropActivity extends AppCompatActivity {
CropUtils.CropImageView imageview1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop);
Button button1 = findViewById(R.id.done_button);
imageview1 = findViewById(R.id.cropimageview);
imageview1.setImageBitmap(getBitmapFromCache());
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmap = imageview1.getCroppedImage();
try {
saveBitmapToCache(bitmap);
} catch (IOException e) {
Log.e("tag", e.toString());
}
finish();
}
});
}
public void saveBitmapToCache(Bitmap bitmap) throws IOException {
String filename = "final_image.jpg";
File cacheFile = new File(getApplicationContext().getCacheDir(), filename);
OutputStream out = new FileOutputStream(cacheFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, (int)100, out);
out.flush();
out.close();
}
public Bitmap getBitmapFromCache(){
File cacheFile = new File(getApplicationContext().getCacheDir(), "final_image.jpg");
Bitmap myBitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath());
return myBitmap;
}
}
Now run the app