添加画布视图控件类用于相册集封面裁剪
- 将CropCanvas改造成CropCanvasView画布视图控件类 - 画布控件有宽度、高度和画布比例属性 - 初始化函数传入原始图片宽度、高度和图片比例 - 计算扩展高度和扩展宽度属性 - 画布高度和宽度取较大值 - 图片居中平铺到画布控件
This commit is contained in:
@@ -0,0 +1,172 @@
|
|||||||
|
package cc.winboll.studio.gallery;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
public class CropActivity extends AppCompatActivity {
|
||||||
|
public static final String TAG = "CropActivity";
|
||||||
|
public static final String EXTRA_IMAGE_URI = "image_uri";
|
||||||
|
public static final String EXTRA_IMAGE_PATH = "image_path";
|
||||||
|
public static final String EXTRA_ALBUM_PATH = "album_path";
|
||||||
|
public static final String EXTRA_CROP_WIDTH = "crop_width";
|
||||||
|
public static final String EXTRA_CROP_HEIGHT = "crop_height";
|
||||||
|
|
||||||
|
private CropCanvasView cropCanvasView;
|
||||||
|
private CropOverlayView cropOverlay;
|
||||||
|
private Bitmap originalBitmap;
|
||||||
|
private String imagePath;
|
||||||
|
private String albumPath;
|
||||||
|
private int cropWidth = 240;
|
||||||
|
private int cropHeight = 120;
|
||||||
|
private float cropRatio = 2.0f;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_crop);
|
||||||
|
|
||||||
|
imagePath = getIntent().getStringExtra(EXTRA_IMAGE_PATH);
|
||||||
|
albumPath = getIntent().getStringExtra(EXTRA_ALBUM_PATH);
|
||||||
|
cropWidth = getIntent().getIntExtra(EXTRA_CROP_WIDTH, 240);
|
||||||
|
cropHeight = getIntent().getIntExtra(EXTRA_CROP_HEIGHT, 120);
|
||||||
|
|
||||||
|
Preferences prefs = new Preferences(this);
|
||||||
|
if (cropWidth > 0 && cropHeight > 0) {
|
||||||
|
cropRatio = (float) cropWidth / cropHeight;
|
||||||
|
} else {
|
||||||
|
cropRatio = prefs.getCoverRatio();
|
||||||
|
cropWidth = (int) (120 * cropRatio);
|
||||||
|
cropHeight = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById(R.id.btn_close).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btn_done).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
saveCroppedCover();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cropCanvasView = findViewById(R.id.crop_canvas_view);
|
||||||
|
cropOverlay = findViewById(R.id.crop_overlay);
|
||||||
|
loadImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadImage() {
|
||||||
|
try {
|
||||||
|
if (imagePath != null && new File(imagePath).exists()) {
|
||||||
|
originalBitmap = BitmapFactory.decodeFile(imagePath);
|
||||||
|
} else {
|
||||||
|
Uri imageUri = getIntent().getParcelableExtra(EXTRA_IMAGE_URI);
|
||||||
|
if (imageUri != null) {
|
||||||
|
originalBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originalBitmap != null) {
|
||||||
|
cropCanvasView.setImageBitmap(originalBitmap);
|
||||||
|
cropCanvasView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
initCropOverlay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "loadImage error: " + e.getMessage());
|
||||||
|
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCropOverlay() {
|
||||||
|
cropCanvasView.initCanvas(originalBitmap.getWidth(), originalBitmap.getHeight(), cropRatio);
|
||||||
|
|
||||||
|
int canvasW = cropCanvasView.getCanvasWidth();
|
||||||
|
int canvasH = cropCanvasView.getCanvasHeight();
|
||||||
|
|
||||||
|
cropOverlay.setTargetRatio(cropRatio);
|
||||||
|
cropOverlay.initCanvas(canvasW, canvasH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveCroppedCover() {
|
||||||
|
if (originalBitmap == null) {
|
||||||
|
Toast.makeText(this, "Failed to get image", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
RectF cropRect = cropOverlay.getCropRect();
|
||||||
|
RectF canvasBounds = cropCanvasView.getCanvasBounds();
|
||||||
|
RectF imageBounds = cropCanvasView.getImageBounds();
|
||||||
|
|
||||||
|
int imgW = originalBitmap.getWidth();
|
||||||
|
int imgH = originalBitmap.getHeight();
|
||||||
|
|
||||||
|
float scaleX = (float) imgW / canvasBounds.width();
|
||||||
|
float scaleY = (float) imgH / canvasBounds.height();
|
||||||
|
|
||||||
|
float canvasLeft = cropRect.left - canvasBounds.left;
|
||||||
|
float canvasTop = cropRect.top - canvasBounds.top;
|
||||||
|
|
||||||
|
int cropX = (int) ((canvasLeft - imageBounds.left) * scaleX);
|
||||||
|
int cropY = (int) ((canvasTop - imageBounds.top) * scaleY);
|
||||||
|
int cropW = (int) (cropRect.width() * scaleX);
|
||||||
|
int cropH = (int) (cropRect.height() * scaleY);
|
||||||
|
|
||||||
|
cropX = Math.max(0, Math.min(cropX, imgW - 1));
|
||||||
|
cropY = Math.max(0, Math.min(cropY, imgH - 1));
|
||||||
|
cropW = Math.max(1, Math.min(cropW, imgW - cropX));
|
||||||
|
cropH = Math.max(1, Math.min(cropH, imgH - cropY));
|
||||||
|
|
||||||
|
Bitmap cropped = Bitmap.createBitmap(originalBitmap, cropX, cropY, cropW, cropH);
|
||||||
|
|
||||||
|
File cacheDir = getCacheDir();
|
||||||
|
File coverFile = new File(cacheDir, "cover_" + System.currentTimeMillis() + ".png");
|
||||||
|
FileOutputStream fos = new FileOutputStream(coverFile);
|
||||||
|
cropped.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
cropped.recycle();
|
||||||
|
|
||||||
|
AlbumCoverDbHelper coverDbHelper = AlbumCoverDbHelper.getInstance(this);
|
||||||
|
coverDbHelper.setCoverWithCrop(albumPath, imagePath, coverFile.getAbsolutePath());
|
||||||
|
|
||||||
|
Toast.makeText(this, "封面已保存", Toast.LENGTH_SHORT).show();
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.e(TAG, "saveCroppedCover error: " + e.getMessage());
|
||||||
|
Toast.makeText(this, "Failed to save cover", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (originalBitmap != null) {
|
||||||
|
originalBitmap.recycle();
|
||||||
|
originalBitmap = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package cc.winboll.studio.gallery;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public class CropCanvasView extends View {
|
||||||
|
private Paint imagePaint;
|
||||||
|
|
||||||
|
private int imageWidth;
|
||||||
|
private int imageHeight;
|
||||||
|
private float coverRatio = 2.0f;
|
||||||
|
|
||||||
|
private int extendHeight;
|
||||||
|
private int extendWidth;
|
||||||
|
private int canvasWidth;
|
||||||
|
private int canvasHeight;
|
||||||
|
|
||||||
|
private RectF imageBounds = new RectF();
|
||||||
|
private RectF canvasBounds = new RectF();
|
||||||
|
private Bitmap originalBitmap;
|
||||||
|
|
||||||
|
public CropCanvasView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CropCanvasView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CropCanvasView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
imagePaint = new Paint();
|
||||||
|
imagePaint.setFilterBitmap(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageBitmap(Bitmap bitmap) {
|
||||||
|
this.originalBitmap = bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initCanvas(int imgWidth, int imgHeight, float ratio) {
|
||||||
|
this.imageWidth = imgWidth;
|
||||||
|
this.imageHeight = imgHeight;
|
||||||
|
this.coverRatio = ratio;
|
||||||
|
|
||||||
|
float imgRatio = (float) imgWidth / imgHeight;
|
||||||
|
|
||||||
|
if (coverRatio >= imgRatio) {
|
||||||
|
extendHeight = imgHeight;
|
||||||
|
extendWidth = (int) (imgHeight * coverRatio);
|
||||||
|
} else {
|
||||||
|
extendWidth = imgWidth;
|
||||||
|
extendHeight = (int) (imgWidth / coverRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasHeight = Math.max(imageHeight, extendHeight);
|
||||||
|
canvasWidth = Math.max(imageWidth, extendWidth);
|
||||||
|
|
||||||
|
float left = (canvasWidth - imageWidth) / 2f;
|
||||||
|
float top = (canvasHeight - imageHeight) / 2f;
|
||||||
|
imageBounds.set(left, top, left + imageWidth, top + imageHeight);
|
||||||
|
canvasBounds.set(0, 0, canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scaleToView(int viewWidth, int viewHeight) {
|
||||||
|
if (viewWidth > 0 && viewHeight > 0 && canvasWidth > 0 && canvasHeight > 0) {
|
||||||
|
float scale = Math.max((float) viewWidth / canvasWidth, (float) viewHeight / canvasHeight);
|
||||||
|
canvasWidth = (int) (canvasWidth * scale);
|
||||||
|
canvasHeight = (int) (canvasHeight * scale);
|
||||||
|
|
||||||
|
float left = (canvasWidth - imageWidth) / 2f;
|
||||||
|
float top = (canvasHeight - imageHeight) / 2f;
|
||||||
|
imageBounds.set(left, top, left + imageWidth, top + imageHeight);
|
||||||
|
canvasBounds.set(0, 0, canvasWidth, canvasHeight);
|
||||||
|
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCanvasWidth() {
|
||||||
|
return canvasWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCanvasHeight() {
|
||||||
|
return canvasHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getExtendWidth() {
|
||||||
|
return extendWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getExtendHeight() {
|
||||||
|
return extendHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RectF getCanvasBounds() {
|
||||||
|
return new RectF(canvasBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RectF getImageBounds() {
|
||||||
|
return new RectF(imageBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getCoverRatio() {
|
||||||
|
return coverRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int w = canvasWidth > 0 ? canvasWidth : 0;
|
||||||
|
int h = canvasHeight > 0 ? canvasHeight : 0;
|
||||||
|
setMeasuredDimension(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
canvas.drawColor(Color.GREEN);
|
||||||
|
|
||||||
|
if (originalBitmap != null && !originalBitmap.isRecycled()) {
|
||||||
|
canvas.drawBitmap(originalBitmap, imageBounds.left, imageBounds.top, imagePaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package cc.winboll.studio.gallery;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public class CropOverlayView extends View {
|
||||||
|
public interface OnCanvasSizeChangedListener {
|
||||||
|
void onCanvasSizeChanged(RectF canvasBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnCanvasSizeChangedListener canvasSizeChangedListener;
|
||||||
|
|
||||||
|
private Paint borderPaint;
|
||||||
|
private Paint cornerPaint;
|
||||||
|
private RectF cropRect;
|
||||||
|
private int touchArea = 50;
|
||||||
|
|
||||||
|
private float lastX, lastY;
|
||||||
|
private int activeCorner = -1;
|
||||||
|
private static final int CORNER_TOP_LEFT = 0;
|
||||||
|
private static final int CORNER_TOP_RIGHT = 1;
|
||||||
|
private static final int CORNER_BOTTOM_LEFT = 2;
|
||||||
|
private static final int CORNER_BOTTOM_RIGHT = 3;
|
||||||
|
private static final int CORNER_CENTER = 4;
|
||||||
|
|
||||||
|
private float targetRatio = 2.0f;
|
||||||
|
private RectF canvasBounds = new RectF();
|
||||||
|
private float minSize = 50;
|
||||||
|
|
||||||
|
public CropOverlayView(Context context) {
|
||||||
|
super(context);
|
||||||
|
initPaints();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CropOverlayView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
initPaints();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CropOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
initPaints();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPaints() {
|
||||||
|
borderPaint = new Paint();
|
||||||
|
borderPaint.setColor(Color.parseColor("#CCAA00"));
|
||||||
|
borderPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
borderPaint.setStrokeWidth(3);
|
||||||
|
|
||||||
|
cornerPaint = new Paint();
|
||||||
|
cornerPaint.setColor(Color.WHITE);
|
||||||
|
cornerPaint.setStyle(Paint.Style.FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetRatio(float ratio) {
|
||||||
|
this.targetRatio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getTargetRatio() {
|
||||||
|
return targetRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnCanvasSizeChangedListener(OnCanvasSizeChangedListener listener) {
|
||||||
|
this.canvasSizeChangedListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initCanvas(int canvasW, int canvasH) {
|
||||||
|
canvasBounds.set(0, 0, canvasW, canvasH);
|
||||||
|
|
||||||
|
cropRect = new RectF(0, 0, canvasW, canvasH);
|
||||||
|
|
||||||
|
if (canvasSizeChangedListener != null) {
|
||||||
|
canvasSizeChangedListener.onCanvasSizeChanged(new RectF(canvasBounds));
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RectF getCropRect() {
|
||||||
|
return new RectF(cropRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RectF getCanvasBounds() {
|
||||||
|
return new RectF(canvasBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
canvas.drawColor(Color.TRANSPARENT);
|
||||||
|
|
||||||
|
if (cropRect != null) {
|
||||||
|
canvas.drawRect(cropRect, borderPaint);
|
||||||
|
|
||||||
|
canvas.drawCircle(cropRect.left, cropRect.top, 12, cornerPaint);
|
||||||
|
canvas.drawCircle(cropRect.right, cropRect.top, 12, cornerPaint);
|
||||||
|
canvas.drawCircle(cropRect.left, cropRect.bottom, 12, cornerPaint);
|
||||||
|
canvas.drawCircle(cropRect.right, cropRect.bottom, 12, cornerPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
if (cropRect == null || canvasBounds.isEmpty()) return false;
|
||||||
|
|
||||||
|
float x = event.getX();
|
||||||
|
float y = event.getY();
|
||||||
|
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
lastX = x;
|
||||||
|
lastY = y;
|
||||||
|
activeCorner = getActiveCorner(x, y);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
float dx = x - lastX;
|
||||||
|
float dy = y - lastY;
|
||||||
|
|
||||||
|
if (activeCorner == CORNER_CENTER) {
|
||||||
|
float newLeft = cropRect.left + dx;
|
||||||
|
float newTop = cropRect.top + dy;
|
||||||
|
float newRight = cropRect.right + dx;
|
||||||
|
float newBottom = cropRect.bottom + dy;
|
||||||
|
|
||||||
|
if (newLeft >= canvasBounds.left && newRight <= canvasBounds.right) {
|
||||||
|
cropRect.left = newLeft;
|
||||||
|
cropRect.right = newRight;
|
||||||
|
}
|
||||||
|
if (newTop >= canvasBounds.top && newBottom <= canvasBounds.bottom) {
|
||||||
|
cropRect.top = newTop;
|
||||||
|
cropRect.bottom = newBottom;
|
||||||
|
}
|
||||||
|
} else if (activeCorner == CORNER_TOP_LEFT) {
|
||||||
|
adjustCorner(cropRect.left + dx, cropRect.top + dy, true, true);
|
||||||
|
} else if (activeCorner == CORNER_TOP_RIGHT) {
|
||||||
|
adjustCorner(cropRect.right + dx, cropRect.top + dy, false, true);
|
||||||
|
} else if (activeCorner == CORNER_BOTTOM_LEFT) {
|
||||||
|
adjustCorner(cropRect.left + dx, cropRect.bottom + dy, true, false);
|
||||||
|
} else if (activeCorner == CORNER_BOTTOM_RIGHT) {
|
||||||
|
adjustCorner(cropRect.right + dx, cropRect.bottom + dy, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastX = x;
|
||||||
|
lastY = y;
|
||||||
|
invalidate();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
activeCorner = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustCorner(float nx, float ny, boolean left, boolean top) {
|
||||||
|
float newWidth;
|
||||||
|
float newLeft = cropRect.left;
|
||||||
|
float newTop = cropRect.top;
|
||||||
|
float newRight = cropRect.right;
|
||||||
|
float newBottom = cropRect.bottom;
|
||||||
|
|
||||||
|
if (left) {
|
||||||
|
newWidth = cropRect.width() - (nx - cropRect.left);
|
||||||
|
newLeft = Math.max(canvasBounds.left, Math.min(nx, cropRect.right - minSize));
|
||||||
|
} else {
|
||||||
|
newWidth = nx - cropRect.left;
|
||||||
|
newRight = Math.min(canvasBounds.right, Math.max(nx, cropRect.left + minSize));
|
||||||
|
newLeft = cropRect.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
float newHeight = newWidth / targetRatio;
|
||||||
|
|
||||||
|
if (top) {
|
||||||
|
newTop = Math.max(canvasBounds.top, Math.min(cropRect.bottom - minSize, cropRect.bottom - newHeight));
|
||||||
|
newBottom = newTop + newHeight;
|
||||||
|
} else {
|
||||||
|
newBottom = Math.min(canvasBounds.bottom, Math.max(cropRect.top + minSize, cropRect.top + newHeight));
|
||||||
|
newTop = newBottom - newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left) {
|
||||||
|
cropRect.left = newLeft;
|
||||||
|
cropRect.right = newLeft + newWidth;
|
||||||
|
} else {
|
||||||
|
cropRect.right = newRight;
|
||||||
|
cropRect.left = newRight - newWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top) {
|
||||||
|
cropRect.top = newTop;
|
||||||
|
cropRect.bottom = newTop + newHeight;
|
||||||
|
} else {
|
||||||
|
cropRect.bottom = newBottom;
|
||||||
|
cropRect.top = newBottom - newHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getActiveCorner(float x, float y) {
|
||||||
|
if (Math.abs(x - cropRect.left) <= touchArea && Math.abs(y - cropRect.top) <= touchArea) {
|
||||||
|
return CORNER_TOP_LEFT;
|
||||||
|
}
|
||||||
|
if (Math.abs(x - cropRect.right) <= touchArea && Math.abs(y - cropRect.top) <= touchArea) {
|
||||||
|
return CORNER_TOP_RIGHT;
|
||||||
|
}
|
||||||
|
if (Math.abs(x - cropRect.left) <= touchArea && Math.abs(y - cropRect.bottom) <= touchArea) {
|
||||||
|
return CORNER_BOTTOM_LEFT;
|
||||||
|
}
|
||||||
|
if (Math.abs(x - cropRect.right) <= touchArea && Math.abs(y - cropRect.bottom) <= touchArea) {
|
||||||
|
return CORNER_BOTTOM_RIGHT;
|
||||||
|
}
|
||||||
|
if (cropRect.contains(x, y)) {
|
||||||
|
return CORNER_CENTER;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
gallery/src/main/res/drawable/ic_close.xml
Normal file
10
gallery/src/main/res/drawable/ic_close.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
||||||
53
gallery/src/main/res/layout/activity_crop.xml
Normal file
53
gallery/src/main/res/layout/activity_crop.xml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@color/colorPrimary"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btn_close"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btn_done"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="完成"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<cc.winboll.studio.gallery.CropCanvasView
|
||||||
|
android:id="@+id/crop_canvas_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
<cc.winboll.studio.gallery.CropOverlayView
|
||||||
|
android:id="@+id/crop_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
8
gallery/src/main/res/menu/menu_crop.xml
Normal file
8
gallery/src/main/res/menu/menu_crop.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_done"
|
||||||
|
android:title="完成"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
</menu>
|
||||||
Reference in New Issue
Block a user