添加裁剪信息窗口功能

- 添加信息按钮和对话框,显示画布、颜色、裁剪信息
- 显示裁剪结果预览图和拾取坐标
- 松开手指时重置拾取按钮状态
This commit is contained in:
2026-04-28 13:54:47 +08:00
parent b035d461aa
commit c49e68d7f1
6 changed files with 185 additions and 28 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View 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>

View File

@@ -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"

View 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>