A friend of the company wrote it and can crop the pictures in any proportion. I think it's very useful. I will record it here briefly and will definitely be used in the future.
public class SeniorCropImageView extends ImageView implements , { /* For drawing color field start */ private static final int LINE_COLOR = ; private static final int OUTER_MASK_COLOR = (191, 0, 0, 0); private static final int LINE_WIDTH_IN_DP = 1; private final float[] mMatrixValues = new float[9]; protected Matrix mSupportMatrix; protected ScaleGestureDetector mScaleGestureDetector; /* For drawing color field end */ protected Paint mPaint; /* * Height aspect ratio */ protected float mRatio = 1.0f; protected RectF mCropRect; //RectFPadding is to adapt to product needs, set padding for the crop box mCropRect -- chenglin April 18, 2016protected float RectFPadding = 0; protected int mLastX; protected int mLastY; protected OPERATION mOperation; private onBitmapLoadListener iBitmapLoading = null; private boolean mEnableDrawCropWidget = true; /* For scale and drag */ private Matrix mBaseMatrix; private Matrix mDrawMatrix; private AccelerateDecelerateInterpolator sInterpolator = new AccelerateDecelerateInterpolator(); private Path mPath; private int mLineWidth; private float mScaleMax = 3.0f; private RectF mBoundaryRect; private int mRotation = 0; private int mImageWidth; private int mImageHeight; private int mDisplayW; private int mDisplayH; public SeniorCropImageView(Context context) { this(context, null); } public SeniorCropImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SeniorCropImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (attrs != null) { TypedArray a = (attrs, .Life_CropImage); mRatio = (.Life_CropImage_life_Crop_ratio, 1.0f); (); } init(); } public static void decodeImageForCropping(final String path, final IDecodeCallback callback) { new Thread(new Runnable() { @Override public void run() { int rotation = 0; // Read the rotation in exiftry { ExifInterface exif = new ExifInterface(path); final int rotate = (ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); switch (rotate) { case ExifInterface.ORIENTATION_ROTATE_90: rotation = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break; } } catch (IOException e) { (); } final options = new (); = true; (path, options); final int textureLimit = getMaxTextureSize(); int scale = 1; while ( / scale >= textureLimit) { scale *= 2; } while ( / scale >= textureLimit) { scale *= 2; } = scale; = false; Bitmap bitmap = null; try { bitmap = (path, options); } catch (OutOfMemoryError e) { (); } final Bitmap bimapDecoded = bitmap; if (bimapDecoded == null) { return; } if (callback != null) { (rotation, bimapDecoded); } } }).start(); } private static int getMaxTextureSize() { EGL10 egl = (EGL10) (); EGLDisplay display = (EGL10.EGL_DEFAULT_DISPLAY); // Initialise int[] version = new int[2]; (display, version); // Query total number of configurations int[] totalConfigurations = new int[1]; (display, null, 0, totalConfigurations); // Query actual list configurations EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]]; (display, configurationsList, totalConfigurations[0], totalConfigurations); int[] textureSize = new int[1]; int maximumTextureSize = 0; // Iterate through all the configurations to located the maximum texture size for (int i = 0; i < totalConfigurations[0]; i++) { // Only need to check for width since opengl textures are always squared (display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize); // Keep track of the maximum texture size if (maximumTextureSize < textureSize[0]) { maximumTextureSize = textureSize[0]; } } // Release (display); return maximumTextureSize; } @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { mDisplayW = right - left; mDisplayH = bottom - top; if (getDrawable() != null && ((BitmapDrawable) getDrawable()).getBitmap() != null) { calculateProperties(((BitmapDrawable) getDrawable()).getBitmap()); } } private void init() { mScaleGestureDetector = new ScaleGestureDetector(getContext(), this); mBaseMatrix = new Matrix(); mDrawMatrix = new Matrix(); mSupportMatrix = new Matrix(); mLineWidth = (int) dipToPixels(LINE_WIDTH_IN_DP); mPaint = new Paint(); // Indicates that the first solid line segment is long dashOnWidth, and the first dotted line segment is long dashOffWidthmPath = new Path(); mCropRect = new RectF(); mBoundaryRect = new RectF(); setScaleType(); setClickable(true); } private float dipToPixels(float dip) { return (TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics()); } @Override protected void onAttachedToWindow() { (); addOnLayoutChangeListener(this); } @Override protected void onDetachedFromWindow() { (); removeOnLayoutChangeListener(this); } /** * Set the crop ratio of the picture, for example, 3:4 is 0.75 * * @param ratio */ public void setCropRatio(final float ratio) { if (mRatio == ratio) { return; } mRatio = ratio; //After reselecting the rotation angle, restore the rotation angle//setImageRotation(0); if (getDrawable() == null) { return; } calculateProperties(((BitmapDrawable) getDrawable()).getBitmap()); postInvalidate(); } public void setImageRotation(int rotation) { if (mRotation == rotation) { return; } mRotation = rotation; if (getDrawable() == null) { return; } calculateProperties(((BitmapDrawable) getDrawable()).getBitmap()); postInvalidate(); } public void setCropRectPadding(float padding) { RectFPadding = padding; } public void setImagePath(final String path) { if ((path)) { return; } if (iBitmapLoading != null) { (); } decodeImageForCropping(path, new IDecodeCallback() { @Override public void onDecoded(final int rotation, final Bitmap bitmap) { post(new Runnable() { @Override public void run() { mRotation = rotation; setImageBitmap(bitmap); if (iBitmapLoading != null) { (); } } }); } }); } @Override public void setImageBitmap(Bitmap bm) { calculateProperties(bm); (bm); } public void setBitmapLoadingListener(onBitmapLoadListener iBitmapLoad) { iBitmapLoading = iBitmapLoad; } protected void calculateProperties(Bitmap bm) { (); (); int widthSize = mDisplayW; int heightSize = mDisplayH; generateCropRect(widthSize, heightSize); mImageWidth = (); mImageHeight = (); final boolean rotated = isImageRotated(); final int bitmapWidth = rotated ? mImageHeight : mImageWidth; final int bitmapHeight = rotated ? mImageWidth : mImageHeight; (0, 0, bitmapWidth, bitmapHeight); final float widthScale = () / bitmapWidth; final float heightScale = () / bitmapHeight; final float scale = (widthScale, heightScale); final float scaledHeight = scale * bitmapHeight; final float scaledWidth = scale * bitmapWidth; // Move to the center pointfinal int translateX = (int) ( + () / 2 - scaledWidth / 2); final int translateY = (int) ( + () / 2 - scaledHeight / 2); (scale, scale); (translateX, translateY); (mBoundaryRect); setImageMatrix(getDrawMatrix()); } private boolean isImageRotated() { return ((mRotation % 360) == 90) || ((mRotation % 360) == 270); } private void generateCropRect(int boundaryWidth, int boundaryHeight) { //RectFPadding is to adapt to product needs, set padding for the crop box mCropRect -- chenglin April 18, 2016boundaryWidth = boundaryWidth - (int)(RectFPadding * 2); boundaryHeight = boundaryHeight - (int)(RectFPadding * 2); int left; int top; int right; int bottom; boolean vertical; // If width/height is greater than the proportion, it means that the cutting frame is "vertical"vertical = (float) boundaryWidth / boundaryHeight > mRatio; final int rectH = (int) (boundaryWidth / mRatio); final int rectW = (int) (boundaryHeight * mRatio); if (vertical) { left = (boundaryWidth - rectW) / 2; top = 0; right = (boundaryWidth + rectW) / 2; bottom = boundaryHeight; } else { left = 0; top = (boundaryHeight - rectH) / 2; right = boundaryWidth; bottom = (boundaryHeight + rectH) / 2; } //RectFPadding is to adapt to product needs, set padding for the crop box mCropRect -- chenglin April 18, 2016(left + RectFPadding, top + RectFPadding, right + RectFPadding, bottom + RectFPadding); } @Override protected void onDraw(Canvas canvas) { (canvas); if (!mEnableDrawCropWidget) { return; } if (getDrawable() == null) { return; } (); (true); (LINE_COLOR); (mLineWidth); (); (); // superior(, ); (, ); // Left(, ); (, ); // Right(, ); (, ); // Down(, ); (, ); (mPath, mPaint); // Draw the external shadow part(); (true); (("#B3333333")); (); //The four rectangles below are decorative, which are the four shadows around the cropped framefinal int lineOffset = mLineWidth; if ( > 0) { (0, 0, getMeasuredWidth(), - lineOffset, mPaint); } if ( > 0) { ( - lineOffset - RectFPadding, RectFPadding - lineOffset, - lineOffset, + lineOffset, mPaint); } if ( < getMeasuredWidth()) { ( + lineOffset, - lineOffset, getMeasuredWidth(), + lineOffset, mPaint); } if ( < getMeasuredHeight()) { (0, + lineOffset, getMeasuredWidth(), getMeasuredHeight(), mPaint); } } public boolean onTouchEvent(MotionEvent ev) { if (() > 1) { mOperation = ; return (ev); } final int action = (); final int x = (int) (); final int y = (int) (); switch (action) { case MotionEvent.ACTION_DOWN: mOperation = ; mLastX = x; mLastY = y; break; case MotionEvent.ACTION_MOVE: if (mOperation == ) { int deltaX = x - mLastX; int deltaY = y - mLastY; RectF boundary = getDrawBoundary(getDrawMatrix()); if ( + deltaX > ) { deltaX = (int) ( - ); } else if ( + deltaX < ) { deltaX = (int) ( - ); } if ( + deltaY > ) { deltaY = (int) ( - ); } else if ( + deltaY < ) { deltaY = (int) ( - ); } (deltaX, deltaY); setImageMatrix(getDrawMatrix()); mLastX = x; mLastY = y; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: mLastX = 0; mLastY = 0; mOperation = null; break; } return (ev); } public Bitmap getOriginBitmap() { BitmapDrawable drawable = (BitmapDrawable) getDrawable(); return drawable == null ? null : (); } /** * Save the image as bitmap */ public Bitmap saveCrop() throws OutOfMemoryError { if (getDrawable() == null) { return null; } Bitmap origin = getOriginBitmap(); Matrix drawMatrix = getDrawMatrix(); // Invert the matrixMatrix inverse = new Matrix(); (inverse); // Correlate the cropping frame to the original imageRectF cropMapped = new RectF(); (cropMapped, mCropRect); clampCropRect(cropMapped, (), ()); // If a rotation is generated, a rotation matrix is requiredMatrix rotationM = new Matrix(); if (mRotation % 360 != 0) { (mRotation, () / 2, () / 2); } Bitmap cropped = ( origin, (int) , (int) , (int) (), (int) (), rotationM, true ); return cropped; } private void clampCropRect(RectF cropRect, int borderW, int borderH) { if ( < 0) { = 0; } if ( < 0) { = 0; } if ( > borderW) { = borderW; } if ( > borderH) { = borderH; } } @Override public boolean onScale(ScaleGestureDetector detector) { float scale = (); if (scale == 1.0f) { return true; } final float currentScale = getScale(mSupportMatrix); final float centerX = (); final float centerY = (); if ((currentScale <= 1.0f && scale < 1.0f) || (currentScale >= mScaleMax && scale > 1.0f)) { return true; } if (currentScale * scale < 1.0f) { scale = 1.0f / currentScale; } else if (currentScale * scale > mScaleMax) { scale = mScaleMax / currentScale; } (scale, scale, centerX, centerY); RectF boundary = getDrawBoundary(getDrawMatrix()); float translateX = 0; if ( > ) { translateX = - ; } else if ( < ) { translateX = - ; } ("scale", "x==>" + translateX); float translateY = 0; if ( > ) { translateY = - ; } else if ( < ) { translateY = - ; } (translateX, translateY); setImageMatrix(getDrawMatrix()); return true; } protected Matrix getDrawMatrix() { (); if (mRotation % 360 != 0) { final boolean rotated = isImageRotated(); final int width = rotated ? mImageHeight : mImageWidth; final int height = rotated ? mImageWidth : mImageHeight; (mRotation, mImageWidth / 2, mImageHeight / 2); if (rotated) { final int translateX = (width - mImageWidth) / 2; final int translateY = (height - mImageHeight) / 2; (translateX, translateY); } } (mBaseMatrix); (mSupportMatrix); return mDrawMatrix; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { final float currentScale = getScale(mSupportMatrix); if (currentScale < 1.0f) { ("onScaleEnd", "currentScale==>" + currentScale); RectF boundary = getDrawBoundary(getDrawMatrix()); post(new AnimatedZoomRunnable(currentScale, 1.0f, (), ())); } } protected RectF getDrawBoundary(Matrix matrix) { Drawable drawable = getDrawable(); if (drawable == null) { return mBoundaryRect; } final int bitmapWidth = (); final int bitmapHeight = (); (0, 0, bitmapWidth, bitmapHeight); (mBoundaryRect); return mBoundaryRect; } public float getScale(Matrix matrix) { return (float) ((float) (getValue(matrix, Matrix.MSCALE_X), 2) + (float) (getValue(matrix, Matrix.MSKEW_Y), 2)); } /** * Helper method that 'unpacks' a Matrix and returns the required value * * @param matrix - Matrix to unpack * @param whichValue - Which value from * to return * @return float - returned value */ private float getValue(Matrix matrix, int whichValue) { (mMatrixValues); return mMatrixValues[whichValue]; } public void enableDrawCropWidget(boolean enable) { mEnableDrawCropWidget = enable; } protected enum OPERATION { DRAG, SCALE } public enum Type { CENTER_CROP, CENTER_INSIDE } public interface IDecodeCallback { void onDecoded(final int rotation, final Bitmap bitmap); } //The setImagePath method takes time and needs to display the progress bar, which is the monitorpublic interface onBitmapLoadListener { void onLoadPrepare(); void onLoadFinish(); } private class AnimatedZoomRunnable implements Runnable { private final float mFocalX, mFocalY; private final long mStartTime; private final float mZoomStart, mZoomEnd; public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { mFocalX = focalX; mFocalY = focalY; mStartTime = (); mZoomStart = currentZoom; mZoomEnd = targetZoom; } @Override public void run() { float t = interpolate(); float scale = mZoomStart + t * (mZoomEnd - mZoomStart); float deltaScale = scale / getScale(mSupportMatrix); (deltaScale, deltaScale, mFocalX, mFocalY); setImageMatrix(getDrawMatrix()); // We haven't hit our target scale yet, so post ourselves again if (t < 1f) { postOnAnimation(this); } } private float interpolate() { float t = 1f * (() - mStartTime) / 200; t = (1f, t); t = (t); return t; } } } <declare-styleable name="Life_CropImage"> <attr name="life_Crop_ratio" format="float" /> <attr name="life_Crop_scale_type" format="enum"> <enum name="life_center_crop" value="0" /> <enum name="life_center_inside" value="1" /> </attr> </declare-styleable>
1. Let this crop box display the picture:
(path);
2. Save the cropped pictures:
Bitmap imageViewBitmap = null; try { imageViewBitmap = (); } catch (OutOfMemoryError e) { imageViewBitmap = (); (mActivity, .life_image_crop_topbar_crop_error, Toast.LENGTH_LONG).show(); }
3. Set the crop ratio:
(3f / 4f);
4. Set the padding of the cropping box:
(0f);
5. The setImagePath method is time-consuming and requires displaying the progress bar. This is listening:
(new () { @Override public void onLoadPrepare() { (); } @Override public void onLoadFinish() { (); } });
The above is the Android code that I bring to you. I hope it will be helpful to you.