添加裁剪信息窗口功能
- 添加信息按钮和对话框,显示画布、颜色、裁剪信息 - 显示裁剪结果预览图和拾取坐标 - 松开手指时重置拾取按钮状态
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Tue Apr 28 10:57:44 HKT 2026
|
#Tue Apr 28 13:52:30 CST 2026
|
||||||
stageCount=8
|
stageCount=8
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.7
|
publishVersion=15.0.7
|
||||||
buildCount=0
|
buildCount=26
|
||||||
baseBetaVersion=15.0.8
|
baseBetaVersion=15.0.8
|
||||||
|
|||||||
@@ -5,13 +5,21 @@ import android.graphics.BitmapFactory;
|
|||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
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.provider.MediaStore;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -78,6 +86,13 @@ public class CropActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btn_info).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
showCropInfoDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
zoomContainer = findViewById(R.id.zoom_container);
|
zoomContainer = findViewById(R.id.zoom_container);
|
||||||
|
|
||||||
SeekBar seekBarZoom = findViewById(R.id.seekbar_zoom);
|
SeekBar seekBarZoom = findViewById(R.id.seekbar_zoom);
|
||||||
@@ -110,6 +125,18 @@ public class CropActivity extends AppCompatActivity {
|
|||||||
cropCanvasView.setOnColorPickedListener(new CropCanvasView.OnColorPickedListener() {
|
cropCanvasView.setOnColorPickedListener(new CropCanvasView.OnColorPickedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onColorPicked(int color) {
|
public void onColorPicked(int color) {
|
||||||
|
int pickX = cropCanvasView.getLastPickImageX();
|
||||||
|
int pickY = cropCanvasView.getLastPickImageY();
|
||||||
|
Toast.makeText(CropActivity.this,
|
||||||
|
"颜色已拾取: #" + String.format("%06X", color & 0xFFFFFF) +
|
||||||
|
" (" + pickX + "," + pickY + ")",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cropCanvasView.setOnColorPickEndListener(new CropCanvasView.OnColorPickEndListener() {
|
||||||
|
@Override
|
||||||
|
public void onColorPickEnd() {
|
||||||
cropCanvasView.setColorPickMode(false);
|
cropCanvasView.setColorPickMode(false);
|
||||||
btnColorPick.setAlpha(1.0f);
|
btnColorPick.setAlpha(1.0f);
|
||||||
}
|
}
|
||||||
@@ -225,6 +252,67 @@ public class CropActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showCropInfoDialog() {
|
||||||
|
StringBuilder info = new StringBuilder();
|
||||||
|
info.append("=== 画布信息 ===\n");
|
||||||
|
info.append("画布宽度: ").append(cropCanvasView.getCanvasWidth()).append("px\n");
|
||||||
|
info.append("画布高度: ").append(cropCanvasView.getCanvasHeight()).append("px\n");
|
||||||
|
|
||||||
|
info.append("\n=== 拾取颜色 ===\n");
|
||||||
|
int bgColor = cropCanvasView.getBackgroundColor();
|
||||||
|
info.append("背景颜色: #").append(String.format("%06X", bgColor & 0xFFFFFF)).append("\n");
|
||||||
|
info.append("拾取坐标: ").append(cropCanvasView.getLastPickImageX()).append(",")
|
||||||
|
.append(cropCanvasView.getLastPickImageY()).append("\n");
|
||||||
|
|
||||||
|
info.append("\n=== 裁剪结果 ===\n");
|
||||||
|
RectF cropRect = cropCanvasView.getCropRect();
|
||||||
|
if (cropRect != null) {
|
||||||
|
info.append("裁剪区域: ").append((int)cropRect.left).append(",")
|
||||||
|
.append((int)cropRect.top).append(",")
|
||||||
|
.append((int)cropRect.right).append(",")
|
||||||
|
.append((int)cropRect.bottom).append("\n");
|
||||||
|
info.append("裁剪宽度: ").append((int)cropRect.width()).append("px\n");
|
||||||
|
info.append("裁剪高度: ").append((int)cropRect.height()).append("px\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap canvasBitmap = cropCanvasView.getCanvasBitmap();
|
||||||
|
final Bitmap[] previewHolder = new Bitmap[1];
|
||||||
|
if (canvasBitmap != null && !canvasBitmap.isRecycled() && cropRect != null) {
|
||||||
|
int bmpX = Math.max(0, (int) cropRect.left);
|
||||||
|
int bmpY = Math.max(0, (int) cropRect.top);
|
||||||
|
int bmpW = Math.min((int) cropRect.width(), canvasBitmap.getWidth() - bmpX);
|
||||||
|
int bmpH = Math.min((int) cropRect.height(), canvasBitmap.getHeight() - bmpY);
|
||||||
|
if (bmpW > 0 && bmpH > 0) {
|
||||||
|
previewHolder[0] = Bitmap.createBitmap(canvasBitmap, bmpX, bmpY, bmpW, bmpH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Bitmap previewBitmap = previewHolder[0];
|
||||||
|
|
||||||
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_crop_info, null);
|
||||||
|
TextView infoText = dialogView.findViewById(R.id.info_text);
|
||||||
|
ImageView previewImage = dialogView.findViewById(R.id.preview_image);
|
||||||
|
infoText.setText(info.toString());
|
||||||
|
if (previewBitmap != null) {
|
||||||
|
previewImage.setImageBitmap(previewBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||||
|
.setTitle("裁剪信息")
|
||||||
|
.setView(dialogView)
|
||||||
|
.setPositiveButton("确定", null)
|
||||||
|
.create();
|
||||||
|
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||||
|
@Override
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
if (previewBitmap != null && !previewBitmap.isRecycled()) {
|
||||||
|
previewBitmap.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ public class CropCanvasView extends View {
|
|||||||
void onColorPicked(int color);
|
void onColorPicked(int color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnColorPickEndListener {
|
||||||
|
void onColorPickEnd();
|
||||||
|
}
|
||||||
|
|
||||||
private OnBackgroundColorChangedListener backgroundColorChangedListener;
|
private OnBackgroundColorChangedListener backgroundColorChangedListener;
|
||||||
private OnColorPickedListener colorPickedListener;
|
private OnColorPickedListener colorPickedListener;
|
||||||
|
private OnColorPickEndListener colorPickEndListener;
|
||||||
|
|
||||||
public void setOnBackgroundColorChangedListener(OnBackgroundColorChangedListener listener) {
|
public void setOnBackgroundColorChangedListener(OnBackgroundColorChangedListener listener) {
|
||||||
this.backgroundColorChangedListener = listener;
|
this.backgroundColorChangedListener = listener;
|
||||||
@@ -30,6 +35,10 @@ public class CropCanvasView extends View {
|
|||||||
public void setOnColorPickedListener(OnColorPickedListener listener) {
|
public void setOnColorPickedListener(OnColorPickedListener listener) {
|
||||||
this.colorPickedListener = listener;
|
this.colorPickedListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOnColorPickEndListener(OnColorPickEndListener listener) {
|
||||||
|
this.colorPickEndListener = listener;
|
||||||
|
}
|
||||||
private Paint imagePaint;
|
private Paint imagePaint;
|
||||||
private Paint borderPaint;
|
private Paint borderPaint;
|
||||||
private Paint cornerPaint;
|
private Paint cornerPaint;
|
||||||
@@ -65,6 +74,8 @@ public class CropCanvasView extends View {
|
|||||||
private int backgroundColor = Color.BLUE;
|
private int backgroundColor = Color.BLUE;
|
||||||
private boolean colorPickMode = false;
|
private boolean colorPickMode = false;
|
||||||
private int previewColor = 0;
|
private int previewColor = 0;
|
||||||
|
private float lastPickX = 0;
|
||||||
|
private float lastPickY = 0;
|
||||||
private float pickX, pickY;
|
private float pickX, pickY;
|
||||||
|
|
||||||
private float displayScale = 1.0f;
|
private float displayScale = 1.0f;
|
||||||
@@ -270,6 +281,40 @@ public class CropCanvasView extends View {
|
|||||||
return backgroundColor;
|
return backgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getPreviewColor() {
|
||||||
|
return previewColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastPickX() {
|
||||||
|
return lastPickX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getLastPickY() {
|
||||||
|
return lastPickY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastPickImageX() {
|
||||||
|
if (originalBitmap == null || originalBitmap.isRecycled()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
float imgX = screenToImageX(lastPickX);
|
||||||
|
float imgY = screenToImageY(lastPickY);
|
||||||
|
int bmpX = (int) ((imgX - imageBounds.left) * displayBitmapScale);
|
||||||
|
int bmpY = (int) ((imgY - imageBounds.top) * displayBitmapScale);
|
||||||
|
return Math.max(0, Math.min(bmpX, originalBitmap.getWidth() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLastPickImageY() {
|
||||||
|
if (originalBitmap == null || originalBitmap.isRecycled()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
float imgX = screenToImageX(lastPickX);
|
||||||
|
float imgY = screenToImageY(lastPickY);
|
||||||
|
int bmpX = (int) ((imgX - imageBounds.left) * displayBitmapScale);
|
||||||
|
int bmpY = (int) ((imgY - imageBounds.top) * displayBitmapScale);
|
||||||
|
return Math.max(0, Math.min(bmpY, originalBitmap.getHeight() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
public void setColorPickMode(boolean enable) {
|
public void setColorPickMode(boolean enable) {
|
||||||
this.colorPickMode = enable;
|
this.colorPickMode = enable;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
@@ -471,30 +516,11 @@ public class CropCanvasView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (colorPickMode) {
|
if (colorPickMode) {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||||
if (originalBitmap != null && !originalBitmap.isRecycled()) {
|
if (originalBitmap != null && !originalBitmap.isRecycled()) {
|
||||||
float imgX = screenToImageX(x);
|
previewColor = getImageColorAt(x, y);
|
||||||
float imgY = screenToImageY(y);
|
lastPickX = x;
|
||||||
int bmpX = (int) ((imgX - imageBounds.left) * displayBitmapScale);
|
lastPickY = y;
|
||||||
int bmpY = (int) ((imgY - imageBounds.top) * displayBitmapScale);
|
|
||||||
bmpX = Math.max(0, Math.min(bmpX, originalBitmap.getWidth() - 1));
|
|
||||||
bmpY = Math.max(0, Math.min(bmpY, originalBitmap.getHeight() - 1));
|
|
||||||
previewColor = originalBitmap.getPixel(bmpX, bmpY);
|
|
||||||
if (backgroundColorChangedListener != null) {
|
|
||||||
backgroundColorChangedListener.onBackgroundColorChanged(previewColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
|
||||||
if (originalBitmap != null && !originalBitmap.isRecycled()) {
|
|
||||||
float imgX = screenToImageX(x);
|
|
||||||
float imgY = screenToImageY(y);
|
|
||||||
int bmpX = (int) ((imgX - imageBounds.left) * displayBitmapScale);
|
|
||||||
int bmpY = (int) ((imgY - imageBounds.top) * displayBitmapScale);
|
|
||||||
bmpX = Math.max(0, Math.min(bmpX, originalBitmap.getWidth() - 1));
|
|
||||||
bmpY = Math.max(0, Math.min(bmpY, originalBitmap.getHeight() - 1));
|
|
||||||
previewColor = originalBitmap.getPixel(bmpX, bmpY);
|
|
||||||
if (backgroundColorChangedListener != null) {
|
if (backgroundColorChangedListener != null) {
|
||||||
backgroundColorChangedListener.onBackgroundColorChanged(previewColor);
|
backgroundColorChangedListener.onBackgroundColorChanged(previewColor);
|
||||||
}
|
}
|
||||||
@@ -502,14 +528,17 @@ public class CropCanvasView extends View {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
if (previewColor != 0) {
|
if (previewColor != 0 && previewColor != Color.TRANSPARENT) {
|
||||||
backgroundColor = previewColor;
|
backgroundColor = previewColor;
|
||||||
if (colorPickedListener != null) {
|
if (colorPickedListener != null) {
|
||||||
colorPickedListener.onColorPicked(backgroundColor);
|
colorPickedListener.onColorPicked(backgroundColor);
|
||||||
}
|
}
|
||||||
previewColor = 0;
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
if (colorPickEndListener != null) {
|
||||||
|
colorPickEndListener.onColorPickEnd();
|
||||||
|
}
|
||||||
|
previewColor = 0;
|
||||||
|
invalidate();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
6
gallery/src/main/res/drawable/bg_dialog.xml
Normal file
6
gallery/src/main/res/drawable/bg_dialog.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="@android:color/black"/>
|
||||||
|
<stroke android:width="2dp" android:color="@android:color/white"/>
|
||||||
|
</shape>
|
||||||
@@ -55,6 +55,14 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btn_info"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_info"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/btn_done"
|
android:id="@+id/btn_done"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
|
|||||||
26
gallery/src/main/res/layout/dialog_crop_info.xml
Normal file
26
gallery/src/main/res/layout/dialog_crop_info.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="@drawable/bg_dialog">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/info_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:fontFamily="monospace"
|
||||||
|
android:textColor="@android:color/white"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/preview_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:background="@android:color/black"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user