PictureSelector 裁剪框添加文字提示(笔记)
创始人
2025-05-29 13:32:25
0

引言:在不引入PictureSelector module的情况下、进行修改。讲人话 就是PictureSelect采用的是implementation xxx.xxxx.xxx 引入方式、在此基础之上进行扩展修改、且不影响其他功能。

注意:PictureSelector 版本<3.0

先看图(符合预期了再往下看):

实现步骤:

一、继承修改OverlayView、重写其onDraw方法(目的就是drawText);

二、复制ucrop_view.xml文件到自己工程的layout中、在ucrop_view.xml中将OverlayView 替换成自己的(ucrop_view.xml 可以在pictureselect库中找到);


原理(不想看可以跳过 手动狗头):

找到裁剪框的Rect、在其顶部和底部drawText、就O了、其中矩形关键字mCropViewRect(RectF);

分析、找到onDraw() 、进入drawDimmedLayer()函数、其中drawDimmedLayer()就是用来画矩形的(就是那个可以拖动的框框)、找到mCropViewRect、在后面DrawText就行了!

具体参考OverlayViewWithText 源码。


以下是已经写好的 直接用吧!

如何使用?:

     //设置裁剪框上下的文字OverlayViewWithText.setPictureUCropText("顶部巴拉巴拉", "底部巴拉巴拉")//设置裁剪框文字距离裁剪框的间距(包含上下文字)OverlayViewWithText.setPictureUCropMargin(10.dp2Px().toFloat())//设置裁剪框文字大小OverlayViewWithText.setPictureUCropTextSize(15f.sp2Px(), 18f.sp2Px())

1、OverlayViewWithText 相关代码在下面: 


import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.OvershootInterpolator;import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;import com.yalantis.ucrop.R;
import com.yalantis.ucrop.callback.OverlayViewChangeListener;
import com.yalantis.ucrop.util.RectUtils;
import com.yalantis.ucrop.view.OverlayView;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;/*** Created by Oleksii Shliama (https://github.com/shliama).* 

* This view is used for drawing the overlay on top of the image. It may have frame, crop guidelines and dimmed area.* This must have LAYER_TYPE_SOFTWARE to draw itself properly.*/ public class OverlayViewWithText extends OverlayView {public static final int FREESTYLE_CROP_MODE_DISABLE = 0;public static final int FREESTYLE_CROP_MODE_ENABLE = 1;public static final int FREESTYLE_CROP_MODE_ENABLE_WITH_PASS_THROUGH = 2;public static final boolean DEFAULT_DRAG_FRAME = true;public static final boolean DEFAULT_SHOW_CROP_FRAME = true;public static final boolean DEFAULT_SHOW_CROP_GRID = true;public static final boolean DEFAULT_CIRCLE_DIMMED_LAYER = false;public static final int DEFAULT_FREESTYLE_CROP_MODE = FREESTYLE_CROP_MODE_DISABLE;public static final int DEFAULT_CROP_GRID_ROW_COUNT = 2;public static final int DEFAULT_CROP_GRID_COLUMN_COUNT = 2;private Paint topTvPaint;private Paint bomTvPaint;private static String topCon = "";private static String bomCon = "";private float topTvWidth;private float bomTvWidth;private int bomTvHeight;private static float tvMargin;private static float topTvMargin;private static float bomTvMargin;private static float topTvSize;private static float bomTvSize;private final RectF mCropViewRect = new RectF();private final RectF mTempRect = new RectF();protected int mThisWidth, mThisHeight;protected float[] mCropGridCorners;protected float[] mCropGridCenter;private int mCropGridRowCount, mCropGridColumnCount;private float mTargetAspectRatio;private float[] mGridPoints = null;private boolean mShowCropFrame, mShowCropGrid;private boolean mCircleDimmedLayer;private int mDimmedColor;private int mDimmedBorderColor;private Path mCircularPath = new Path();private Paint mDimmedStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);private Paint mCropGridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);private Paint mCropFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);private Paint mCropFrameCornersPaint = new Paint(Paint.ANTI_ALIAS_FLAG);@FreestyleModeprivate int mFreestyleCropMode = DEFAULT_FREESTYLE_CROP_MODE;private float mPreviousTouchX = -1, mPreviousTouchY = -1;private int mCurrentTouchCornerIndex = -1;private int mTouchPointThreshold;private int mCropRectMinSize;private int mCropRectCornerTouchAreaLineLength;private int mStrokeWidth = 1;private boolean mIsDragFrame = DEFAULT_DRAG_FRAME;private ValueAnimator smoothAnimator;private OverlayViewChangeListener mCallback;private boolean mShouldSetupCropBounds;{mTouchPointThreshold = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_corner_touch_threshold);mCropRectMinSize = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_min_size);mCropRectCornerTouchAreaLineLength = getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_rect_corner_touch_area_line_length);}public OverlayViewWithText(Context context) {this(context, null);}public OverlayViewWithText(Context context, AttributeSet attrs) {this(context, attrs, 0);}public OverlayViewWithText(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);topTvPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//默认字体大小if (topTvSize <= 0) topTvSize = 50f;if (bomTvSize <= 0) bomTvSize = 50f;topTvPaint.setTextSize(topTvSize);topTvPaint.setColor(Color.RED);bomTvPaint = new Paint(Paint.ANTI_ALIAS_FLAG);bomTvPaint.setTextSize(bomTvSize);bomTvPaint.setColor(Color.RED);topTvWidth = topTvPaint.measureText(topCon);bomTvWidth = bomTvPaint.measureText(bomCon);Rect bounds = new Rect();bomTvPaint.getTextBounds(bomCon, 0, bomCon.length(), bounds);bomTvHeight = bounds.height();Log.d("over_text", "topw bomw" + topTvWidth + "-" + bomTvWidth + " bomTvHeight " + bomTvHeight);init();}public OverlayViewChangeListener getOverlayViewChangeListener() {return mCallback;}public void setOverlayViewChangeListener(OverlayViewChangeListener callback) {mCallback = callback;}@NonNullpublic RectF getCropViewRect() {return mCropViewRect;}@Deprecated/**** Please use the new method {@link #getFreestyleCropMode() getFreestyleCropMode} method as we have more than 1 freestyle crop mode.*/public boolean isFreestyleCropEnabled() {return mFreestyleCropMode == FREESTYLE_CROP_MODE_ENABLE;}//设置裁剪框上下文本间距public static void setPictureUCropText(String textTop, String textBom) {topCon = textTop;bomCon = textBom;}//设置文字顶部底部marginpublic static void setPictureUCropMargin(float margin) {tvMargin = margin;}//设置文字顶部marginpublic static void setPictureUCropTopMargin(float margin) {topTvMargin = margin;}//设置文字底部marginpublic static void setPictureUCropBomMargin(float margin) {bomTvMargin = margin;}//设置裁剪字体大小public static void setPictureUCropTextSize(float topTextSize, float bomTextSize) {topTvSize = topTextSize;bomTvSize = bomTextSize;}/*** 重置裁剪文案* bomCon topCon 是否为空字符串、决定了是否绘制裁剪框文案;*/public static void reSetUCropText() {topCon = "";bomCon = "";topTvSize = 0f;bomTvSize = 0f;tvMargin = 0;topTvMargin = 0;bomTvMargin = 0;}@Deprecated/**** Please use the new method {@link #setFreestyleCropMode setFreestyleCropMode} method as we have more than 1 freestyle crop mode.*/public void setFreestyleCropEnabled(boolean freestyleCropEnabled) {mFreestyleCropMode = freestyleCropEnabled ? FREESTYLE_CROP_MODE_ENABLE : FREESTYLE_CROP_MODE_DISABLE;}public boolean isDragFrame() {return mIsDragFrame;}public void setDragFrame(boolean mIsDragFrame) {this.mIsDragFrame = mIsDragFrame;}/*** Setter for {@link #mDimmedColor} variable.** @param strokeWidth*/public void setDimmedStrokeWidth(int strokeWidth) {mStrokeWidth = strokeWidth;if (mDimmedStrokePaint != null) {mDimmedStrokePaint.setStrokeWidth(mStrokeWidth);}}@FreestyleModepublic int getFreestyleCropMode() {return mFreestyleCropMode;}public void setFreestyleCropMode(@FreestyleMode int mFreestyleCropMode) {this.mFreestyleCropMode = mFreestyleCropMode;postInvalidate();}/*** Setter for {@link #mCircleDimmedLayer} variable.** @param circleDimmedLayer - set it to true if you want dimmed layer to be an circle*/public void setCircleDimmedLayer(boolean circleDimmedLayer) {mCircleDimmedLayer = circleDimmedLayer;}/*** Setter for crop grid rows count.* Resets {@link #mGridPoints} variable because it is not valid anymore.*/public void setCropGridRowCount(@IntRange(from = 0) int cropGridRowCount) {mCropGridRowCount = cropGridRowCount;mGridPoints = null;}/*** Setter for crop grid columns count.* Resets {@link #mGridPoints} variable because it is not valid anymore.*/public void setCropGridColumnCount(@IntRange(from = 0) int cropGridColumnCount) {mCropGridColumnCount = cropGridColumnCount;mGridPoints = null;}/*** Setter for {@link #mShowCropFrame} variable.** @param showCropFrame - set to true if you want to see a crop frame rectangle on top of an image*/public void setShowCropFrame(boolean showCropFrame) {mShowCropFrame = showCropFrame;}/*** Setter for {@link #mShowCropGrid} variable.** @param showCropGrid - set to true if you want to see a crop grid on top of an image*/public void setShowCropGrid(boolean showCropGrid) {mShowCropGrid = showCropGrid;}/*** Setter for {@link #mDimmedColor} variable.** @param dimmedColor - desired color of dimmed area around the crop bounds*/public void setDimmedColor(@ColorInt int dimmedColor) {mDimmedColor = dimmedColor;}/*** Setter for crop frame stroke width*/public void setCropFrameStrokeWidth(@IntRange(from = 0) int width) {mCropFramePaint.setStrokeWidth(width);}/*** Setter for crop grid stroke width*/public void setCropGridStrokeWidth(@IntRange(from = 0) int width) {mCropGridPaint.setStrokeWidth(width);}/*** Setter for {@link #mDimmedColor} variable.** @param dimmedBorderColor - desired color of dimmed area around the crop bounds*/public void setDimmedBorderColor(@ColorInt int dimmedBorderColor) {mDimmedBorderColor = dimmedBorderColor;if (mDimmedStrokePaint != null) {mDimmedStrokePaint.setColor(mDimmedBorderColor);}}/*** Setter for crop frame color*/public void setCropFrameColor(@ColorInt int color) {mCropFramePaint.setColor(color);}/*** Setter for crop grid color*/public void setCropGridColor(@ColorInt int color) {mCropGridPaint.setColor(color);}/*** This method sets aspect ratio for crop bounds.** @param targetAspectRatio - aspect ratio for image crop (e.g. 1.77(7) for 16:9)*/public void setTargetAspectRatio(final float targetAspectRatio) {mTargetAspectRatio = targetAspectRatio;if (mThisWidth > 0) {setupCropBounds();postInvalidate();} else {mShouldSetupCropBounds = true;}}/*** This method setups crop bounds rectangles for given aspect ratio and view size.* {@link #mCropViewRect} is used to draw crop bounds - uses padding.*/public void setupCropBounds() {int height = (int) (mThisWidth / mTargetAspectRatio);if (height > mThisHeight) {int width = (int) (mThisHeight * mTargetAspectRatio);int halfDiff = (mThisWidth - width) / 2;mCropViewRect.set(getPaddingLeft() + halfDiff, getPaddingTop(),getPaddingLeft() + width + halfDiff, getPaddingTop() + mThisHeight);} else {int halfDiff = (mThisHeight - height) / 2;mCropViewRect.set(getPaddingLeft(), getPaddingTop() + halfDiff,getPaddingLeft() + mThisWidth, getPaddingTop() + height + halfDiff);}if (mCallback != null) {mCallback.onCropRectUpdated(mCropViewRect);}updateGridPoints();}private void updateGridPoints() {mCropGridCorners = RectUtils.getCornersFromRect(mCropViewRect);mCropGridCenter = RectUtils.getCenterFromRect(mCropViewRect);mGridPoints = null;mCircularPath.reset();mCircularPath.addCircle(mCropViewRect.centerX(), mCropViewRect.centerY(),Math.min(mCropViewRect.width(), mCropViewRect.height()) / 2.f, Path.Direction.CW);}protected void init() {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {setLayerType(LAYER_TYPE_SOFTWARE, null);}}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (changed) {left = getPaddingLeft();top = getPaddingTop();right = getWidth() - getPaddingRight();bottom = getHeight() - getPaddingBottom();mThisWidth = right - left;mThisHeight = bottom - top;if (mShouldSetupCropBounds) {mShouldSetupCropBounds = false;setTargetAspectRatio(mTargetAspectRatio);}}}/*** Along with image there are dimmed layer, crop bounds and crop guidelines that must be drawn.*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawDimmedLayer(canvas);drawCropGrid(canvas);}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mCropViewRect.isEmpty() || mFreestyleCropMode == FREESTYLE_CROP_MODE_DISABLE) {return false;}float x = event.getX();float y = event.getY();if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {if (smoothAnimator != null) {smoothAnimator.cancel();}mCurrentTouchCornerIndex = getCurrentTouchIndex(x, y);boolean shouldHandle = mCurrentTouchCornerIndex != -1 && mCurrentTouchCornerIndex != 4;if (!shouldHandle) {mPreviousTouchX = -1;mPreviousTouchY = -1;} else if (mPreviousTouchX < 0) {mPreviousTouchX = x;mPreviousTouchY = y;}return shouldHandle;}if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) {if (event.getPointerCount() == 1 && mCurrentTouchCornerIndex != -1) {x = Math.min(Math.max(x, getPaddingLeft()), getWidth() - getPaddingRight());y = Math.min(Math.max(y, getPaddingTop()), getHeight() - getPaddingBottom());updateCropViewRect(x, y);mPreviousTouchX = x;mPreviousTouchY = y;return true;}}if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {mPreviousTouchX = -1;mPreviousTouchY = -1;mCurrentTouchCornerIndex = -1;if (mCallback != null) {mCallback.onCropRectUpdated(mCropViewRect);}smoothToCenter();}return false;}/*** * The order of the corners is:* 0------->1* ^ |* | 4 |* | v* 3<-------2*/private void updateCropViewRect(float touchX, float touchY) {mTempRect.set(mCropViewRect);switch (mCurrentTouchCornerIndex) {// resize rectanglecase 0:// 是否可拖动裁剪框if (isDragFrame()) {mTempRect.set(touchX, touchY, mCropViewRect.right, mCropViewRect.bottom);}break;case 1:// 是否可拖动裁剪框if (isDragFrame()) {mTempRect.set(mCropViewRect.left, touchY, touchX, mCropViewRect.bottom);}break;case 2:// 是否可拖动裁剪框if (isDragFrame()) {mTempRect.set(mCropViewRect.left, mCropViewRect.top, touchX, touchY);}break;case 3:// 是否可拖动裁剪框if (isDragFrame()) {mTempRect.set(touchX, mCropViewRect.top, mCropViewRect.right, touchY);}break;// move rectanglecase 4:mTempRect.offset(touchX - mPreviousTouchX, touchY - mPreviousTouchY);if (mTempRect.left > getLeft() && mTempRect.top > getTop()&& mTempRect.right < getRight() && mTempRect.bottom < getBottom()) {mCropViewRect.set(mTempRect);updateGridPoints();postInvalidate();}return;}boolean changeHeight = mTempRect.height() >= mCropRectMinSize;boolean changeWidth = mTempRect.width() >= mCropRectMinSize;mCropViewRect.set(changeWidth ? mTempRect.left : mCropViewRect.left,changeHeight ? mTempRect.top : mCropViewRect.top,changeWidth ? mTempRect.right : mCropViewRect.right,changeHeight ? mTempRect.bottom : mCropViewRect.bottom);if (changeHeight || changeWidth) {updateGridPoints();postInvalidate();}}/*** * The order of the corners in the float array is:* 0------->1* ^ |* | 4 |* | v* 3<-------2** @return - index of corner that is being dragged*/private int getCurrentTouchIndex(float touchX, float touchY) {int closestPointIndex = -1;double closestPointDistance = mTouchPointThreshold;for (int i = 0; i < 8; i += 2) {double distanceToCorner = Math.sqrt(Math.pow(touchX - mCropGridCorners[i], 2)+ Math.pow(touchY - mCropGridCorners[i + 1], 2));if (distanceToCorner < closestPointDistance) {closestPointDistance = distanceToCorner;closestPointIndex = i / 2;}}if (mFreestyleCropMode == FREESTYLE_CROP_MODE_ENABLE && closestPointIndex < 0 && mCropViewRect.contains(touchX, touchY)) {return 4;}// for (int i = 0; i <= 8; i += 2) { // // double distanceToCorner; // if (i < 8) { // corners // distanceToCorner = Math.sqrt(Math.pow(touchX - mCropGridCorners[i], 2) // + Math.pow(touchY - mCropGridCorners[i + 1], 2)); // } else { // center // distanceToCorner = Math.sqrt(Math.pow(touchX - mCropGridCenter[0], 2) // + Math.pow(touchY - mCropGridCenter[1], 2)); // } // if (distanceToCorner < closestPointDistance) { // closestPointDistance = distanceToCorner; // closestPointIndex = i / 2; // } // }return closestPointIndex;}/*** This method draws dimmed area around the crop bounds.** @param canvas - valid canvas object*/protected void drawDimmedLayer(@NonNull Canvas canvas) {canvas.save();if (mCircleDimmedLayer) {canvas.clipPath(mCircularPath, Region.Op.DIFFERENCE);} else {canvas.clipRect(mCropViewRect, Region.Op.DIFFERENCE);}canvas.drawColor(mDimmedColor);canvas.restore();if (mCircleDimmedLayer) { // Draw 1px stroke to fix antialiascanvas.drawCircle(mCropViewRect.centerX(), mCropViewRect.centerY(),Math.min(mCropViewRect.width(), mCropViewRect.height()) / 2.f, mDimmedStrokePaint);}if (!topCon.equals("")) {//drawTopTextfloat centerx = mCropViewRect.centerX() - topTvWidth / 2;float tmargin;if (topTvMargin != 0) tmargin = topTvMargin;else tmargin = tvMargin;canvas.drawText(topCon, centerx, mCropViewRect.top - tmargin, topTvPaint);Log.d("over_text ", "draw text " + mCropViewRect + " tmargin " + tmargin);}if (!bomCon.equals("")) {//drawbomTexgfloat bomCenterx = mCropViewRect.centerX() - bomTvWidth / 2;float bmargin;if (bomTvMargin != 0) bmargin = bomTvMargin;else bmargin = tvMargin;canvas.drawText(bomCon, bomCenterx, mCropViewRect.bottom + bomTvHeight + bmargin, bomTvPaint);Log.d("over_text ", "draw text " + mCropViewRect + " bmargin " + bmargin);}}/*** This method draws crop bounds (empty rectangle)* and crop guidelines (vertical and horizontal lines inside the crop bounds) if needed.** @param canvas - valid canvas object*/protected void drawCropGrid(@NonNull Canvas canvas) {if (mShowCropGrid) {if (mGridPoints == null && !mCropViewRect.isEmpty()) {mGridPoints = new float[(mCropGridRowCount) * 4 + (mCropGridColumnCount) * 4];int index = 0;for (int i = 0; i < mCropGridRowCount; i++) {mGridPoints[index++] = mCropViewRect.left;mGridPoints[index++] = (mCropViewRect.height() * (((float) i + 1.0f) / (float) (mCropGridRowCount + 1))) + mCropViewRect.top;mGridPoints[index++] = mCropViewRect.right;mGridPoints[index++] = (mCropViewRect.height() * (((float) i + 1.0f) / (float) (mCropGridRowCount + 1))) + mCropViewRect.top;}for (int i = 0; i < mCropGridColumnCount; i++) {mGridPoints[index++] = (mCropViewRect.width() * (((float) i + 1.0f) / (float) (mCropGridColumnCount + 1))) + mCropViewRect.left;mGridPoints[index++] = mCropViewRect.top;mGridPoints[index++] = (mCropViewRect.width() * (((float) i + 1.0f) / (float) (mCropGridColumnCount + 1))) + mCropViewRect.left;mGridPoints[index++] = mCropViewRect.bottom;}}if (mGridPoints != null) {canvas.drawLines(mGridPoints, mCropGridPaint);}}if (mShowCropFrame) {canvas.drawRect(mCropViewRect, mCropFramePaint);}if (mFreestyleCropMode != FREESTYLE_CROP_MODE_DISABLE) {canvas.save();mTempRect.set(mCropViewRect);mTempRect.inset(mCropRectCornerTouchAreaLineLength, -mCropRectCornerTouchAreaLineLength);canvas.clipRect(mTempRect, Region.Op.DIFFERENCE);mTempRect.set(mCropViewRect);mTempRect.inset(-mCropRectCornerTouchAreaLineLength, mCropRectCornerTouchAreaLineLength);canvas.clipRect(mTempRect, Region.Op.DIFFERENCE);canvas.drawRect(mCropViewRect, mCropFrameCornersPaint);canvas.restore();}}/*** This method extracts all needed values from the styled attributes.* Those are used to configure the view.*/@SuppressWarnings("deprecation")protected void processStyledAttributes(@NonNull TypedArray a) {mCircleDimmedLayer = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_circle_dimmed_layer, DEFAULT_CIRCLE_DIMMED_LAYER);mDimmedColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_dimmed_color,getResources().getColor(R.color.ucrop_color_default_dimmed));mDimmedStrokePaint.setColor(mDimmedBorderColor);mDimmedStrokePaint.setStyle(Paint.Style.STROKE);mDimmedStrokePaint.setStrokeWidth(mStrokeWidth);initCropFrameStyle(a);mShowCropFrame = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_show_frame, DEFAULT_SHOW_CROP_FRAME);initCropGridStyle(a);mShowCropGrid = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_show_grid, DEFAULT_SHOW_CROP_GRID);}/*** This method setups Paint object for the crop bounds.*/@SuppressWarnings("deprecation")private void initCropFrameStyle(@NonNull TypedArray a) {int cropFrameStrokeSize = a.getDimensionPixelSize(R.styleable.ucrop_UCropView_ucrop_frame_stroke_size,getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_frame_stoke_width));int cropFrameColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_frame_color,getResources().getColor(R.color.ucrop_color_default_crop_frame));mCropFramePaint.setStrokeWidth(cropFrameStrokeSize);mCropFramePaint.setColor(cropFrameColor);mCropFramePaint.setStyle(Paint.Style.STROKE);mCropFrameCornersPaint.setStrokeWidth(cropFrameStrokeSize * 3);mCropFrameCornersPaint.setColor(cropFrameColor);mCropFrameCornersPaint.setStyle(Paint.Style.STROKE);}/*** This method setups Paint object for the crop guidelines.*/@SuppressWarnings("deprecation")private void initCropGridStyle(@NonNull TypedArray a) {int cropGridStrokeSize = a.getDimensionPixelSize(R.styleable.ucrop_UCropView_ucrop_grid_stroke_size,getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_grid_stoke_width));int cropGridColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_grid_color,getResources().getColor(R.color.ucrop_color_default_crop_grid));mCropGridPaint.setStrokeWidth(cropGridStrokeSize);mCropGridPaint.setColor(cropGridColor);mCropGridRowCount = a.getInt(R.styleable.ucrop_UCropView_ucrop_grid_row_count, DEFAULT_CROP_GRID_ROW_COUNT);mCropGridColumnCount = a.getInt(R.styleable.ucrop_UCropView_ucrop_grid_column_count, DEFAULT_CROP_GRID_COLUMN_COUNT);}@Retention(RetentionPolicy.SOURCE)@IntDef({FREESTYLE_CROP_MODE_DISABLE, FREESTYLE_CROP_MODE_ENABLE, FREESTYLE_CROP_MODE_ENABLE_WITH_PASS_THROUGH})public @interface FreestyleMode {}/*** 平滑移动至中心*/private void smoothToCenter() {Point centerPoint = new Point((getRight() + getLeft()) / 2, (getTop() + getBottom()) / 2);final int offsetY = (int) (centerPoint.y - mCropViewRect.centerY());final int offsetX = (int) (centerPoint.x - mCropViewRect.centerX());final RectF before = new RectF(mCropViewRect);Log.d("pisa", "pre" + mCropViewRect);RectF after = new RectF(mCropViewRect);after.offset(offsetX, offsetY);Log.d("pisa", "after" + after);if (smoothAnimator != null) {smoothAnimator.cancel();}smoothAnimator = ValueAnimator.ofFloat(0, 1);smoothAnimator.setDuration(1000);smoothAnimator.setInterpolator(new OvershootInterpolator());smoothAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {float lastAnimationValue = 0f;@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float x = offsetX * (float) animation.getAnimatedValue();float y = offsetY * (float) animation.getAnimatedValue();mCropViewRect.set(new RectF(before.left + x,before.top + y,before.right + x,before.bottom + y));updateGridPoints();postInvalidate();if (mCallback != null) {mCallback.postTranslate(offsetX * ((float) animation.getAnimatedValue() - lastAnimationValue),offsetY * ((float) animation.getAnimatedValue() - lastAnimationValue));}lastAnimationValue = (float) animation.getAnimatedValue();}});smoothAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {if (mCallback != null) {mCallback.onCropRectUpdated(mCropViewRect);}}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});smoothAnimator.start();} }


2、ucrop_view.xml



字体颜色自己扩展吧、懒得写方法了;

至此over!

相关内容

热门资讯

豪掷14.83亿元!天原股份拟... 中经记者 陈家运 北京报道2025年12月30日,天原股份(002386.SZ)发布公告称,其全资子...
第五套上市标准扩围后5天迎来首... 中经记者 孙汝祥 夏欣 北京报道上交所官网显示,2025年12月31日,蓝箭航天空间科技股份有限公司...
兆芯集成连年“失血” 控股股东... 《金证研》北方资本中心 亚一BR&*DL/作者夕山 映蔚/风控自申报科创板上市申请获受理已过半年,上...
重拾初心:让春天不再寂静! “我们现在正站在两条路的交叉口上。我们长期以来一直行驶的那条路,看似舒适平坦的高速公路,能让我们疾驰...
2025年A股复盘:成长引领的... 回顾今年,A股在政策支持、产业升级与资金流入的共同驱动下,走出了一轮整体上涨、结构分化的“成长牛”行...
北交所将修订上市规则,强化公司... 新京报贝壳财经讯(记者黄鑫宇)12月31日晚,北京证券交易所(即北交所)宣布,为完成《北京证券交易所...
上市公司全面废除监事会 审计委... 中经记者 郭婧婷 北京报道2026年1月1日,上市公司将全面告别监事会制度,审计委员会接替监事会职能...
圣元环保六千万买私募巨亏,称招... 认购的私募产品出现重大损失,知悉后未及时予以披露,圣元环保及公司多位高管被厦门证监局出具警示函。12...
日本本州东部附近海域发生6.0... 据中国地震台网正式测定,12月31日22时26分在日本本州东部附近海域发生6.0级地震,震源深度20...
德马科技:违规使用募投金支付土... 12月31日,德马科技(688360.SH)发布关于收到浙江证监局行政监管措施决定书的公告。公告称,...