From c49e68d7f149e2d28d0f8328f749ade45c3a520d Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Tue, 28 Apr 2026 13:54:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A3=81=E5=89=AA=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E7=AA=97=E5=8F=A3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加信息按钮和对话框,显示画布、颜色、裁剪信息 - 显示裁剪结果预览图和拾取坐标 - 松开手指时重置拾取按钮状态 --- gallery/build.properties | 4 +- .../winboll/studio/gallery/CropActivity.java | 88 +++++++++++++++++++ .../studio/gallery/CropCanvasView.java | 81 +++++++++++------ gallery/src/main/res/drawable/bg_dialog.xml | 6 ++ gallery/src/main/res/layout/activity_crop.xml | 8 ++ .../src/main/res/layout/dialog_crop_info.xml | 26 ++++++ 6 files changed, 185 insertions(+), 28 deletions(-) create mode 100644 gallery/src/main/res/drawable/bg_dialog.xml create mode 100644 gallery/src/main/res/layout/dialog_crop_info.xml diff --git a/gallery/build.properties b/gallery/build.properties index 3508e42..401922c 100644 --- a/gallery/build.properties +++ b/gallery/build.properties @@ -1,8 +1,8 @@ #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 libraryProject= baseVersion=15.0 publishVersion=15.0.7 -buildCount=0 +buildCount=26 baseBetaVersion=15.0.8 diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/CropActivity.java b/gallery/src/main/java/cc/winboll/studio/gallery/CropActivity.java index 68e9b03..8f3f14d 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/CropActivity.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/CropActivity.java @@ -5,13 +5,21 @@ import android.graphics.BitmapFactory; import android.graphics.RectF; import android.net.Uri; import android.os.Bundle; +import android.content.DialogInterface; 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.view.View; import android.widget.ImageView; import android.widget.ScrollView; import android.widget.SeekBar; +import android.widget.TextView; import android.widget.Toast; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import cc.winboll.studio.libappbase.LogUtils; 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); SeekBar seekBarZoom = findViewById(R.id.seekbar_zoom); @@ -110,6 +125,18 @@ public class CropActivity extends AppCompatActivity { cropCanvasView.setOnColorPickedListener(new CropCanvasView.OnColorPickedListener() { @Override 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); 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 protected void onDestroy() { super.onDestroy(); diff --git a/gallery/src/main/java/cc/winboll/studio/gallery/CropCanvasView.java b/gallery/src/main/java/cc/winboll/studio/gallery/CropCanvasView.java index 9053108..ff35972 100644 --- a/gallery/src/main/java/cc/winboll/studio/gallery/CropCanvasView.java +++ b/gallery/src/main/java/cc/winboll/studio/gallery/CropCanvasView.java @@ -20,8 +20,13 @@ public class CropCanvasView extends View { void onColorPicked(int color); } + public interface OnColorPickEndListener { + void onColorPickEnd(); + } + private OnBackgroundColorChangedListener backgroundColorChangedListener; private OnColorPickedListener colorPickedListener; + private OnColorPickEndListener colorPickEndListener; public void setOnBackgroundColorChangedListener(OnBackgroundColorChangedListener listener) { this.backgroundColorChangedListener = listener; @@ -30,6 +35,10 @@ public class CropCanvasView extends View { public void setOnColorPickedListener(OnColorPickedListener listener) { this.colorPickedListener = listener; } + + public void setOnColorPickEndListener(OnColorPickEndListener listener) { + this.colorPickEndListener = listener; + } private Paint imagePaint; private Paint borderPaint; private Paint cornerPaint; @@ -65,6 +74,8 @@ public class CropCanvasView extends View { private int backgroundColor = Color.BLUE; private boolean colorPickMode = false; private int previewColor = 0; + private float lastPickX = 0; + private float lastPickY = 0; private float pickX, pickY; private float displayScale = 1.0f; @@ -270,6 +281,40 @@ public class CropCanvasView extends View { 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) { this.colorPickMode = enable; if (enable) { @@ -471,30 +516,11 @@ public class CropCanvasView extends View { } if (colorPickMode) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (event.getAction() == MotionEvent.ACTION_DOWN || 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) { - 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); + previewColor = getImageColorAt(x, y); + lastPickX = x; + lastPickY = y; if (backgroundColorChangedListener != null) { backgroundColorChangedListener.onBackgroundColorChanged(previewColor); } @@ -502,14 +528,17 @@ public class CropCanvasView extends View { return true; } if (event.getAction() == MotionEvent.ACTION_UP) { - if (previewColor != 0) { + if (previewColor != 0 && previewColor != Color.TRANSPARENT) { backgroundColor = previewColor; if (colorPickedListener != null) { colorPickedListener.onColorPicked(backgroundColor); } - previewColor = 0; - invalidate(); } + if (colorPickEndListener != null) { + colorPickEndListener.onColorPickEnd(); + } + previewColor = 0; + invalidate(); return true; } return true; diff --git a/gallery/src/main/res/drawable/bg_dialog.xml b/gallery/src/main/res/drawable/bg_dialog.xml new file mode 100644 index 0000000..5477c09 --- /dev/null +++ b/gallery/src/main/res/drawable/bg_dialog.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/gallery/src/main/res/layout/activity_crop.xml b/gallery/src/main/res/layout/activity_crop.xml index ca85d7c..6679297 100644 --- a/gallery/src/main/res/layout/activity_crop.xml +++ b/gallery/src/main/res/layout/activity_crop.xml @@ -55,6 +55,14 @@ android:layout_height="0dp" android:layout_weight="1"/> + + + + + + + + + \ No newline at end of file