图片选择与剪裁阶段调试完成
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Tue Dec 02 06:01:03 GMT 2025
|
||||
#Wed Dec 03 08:28:19 GMT 2025
|
||||
stageCount=13
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=110
|
||||
buildCount=121
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -115,7 +115,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
selectTempDir.mkdirs();
|
||||
LogUtils.d(TAG, "【选图初始化】选图临时目录创建完成:" + selectTempDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
// 初始化UI及数据
|
||||
initToolbar();
|
||||
initClickListeners();
|
||||
@@ -181,7 +181,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean isImageType(String lowerMimeType) {
|
||||
return lowerMimeType.equals("image/jpeg")
|
||||
|| lowerMimeType.equals("image/png")
|
||||
@@ -237,7 +237,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "【按钮点击】触发选择图片功能");
|
||||
|
||||
|
||||
// 调用权限工具类校验存储权限
|
||||
if (mPermissionUtils.checkAndRequestStoragePermission(BackgroundSettingsActivity.this)) {
|
||||
LogUtils.d(TAG, "【选图权限】存储权限已获取,开始查找图片选择意图");
|
||||
@@ -483,10 +483,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(false);
|
||||
mBgSourceUtils.saveSettings();
|
||||
|
||||
|
||||
doubleRefreshPreview();
|
||||
|
||||
File previewFile = mBgSourceUtils.getCropSourceFile(); // 裁剪缓存图片
|
||||
|
||||
File previewFile = new File(previewBean.getBackgroundFilePath()); // 裁剪缓存图片
|
||||
|
||||
// 2. 生成裁剪输入Uri(强化MIUI权限授予,直接用原图Uri)
|
||||
Uri inputUri = null;
|
||||
@@ -503,7 +503,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
// 3. 【核心优化】直接使用预览图原图作为裁剪输入,删除原工具类创建裁剪路径的复制逻辑
|
||||
// 仅创建裁剪结果临时文件(用于接收裁剪输出,不涉及原图复制)
|
||||
File cropResultTempFile = mBgSourceUtils.getCropResultFile();
|
||||
File cropResultTempFile = new File(previewBean.getBackgroundScaledCompressFilePath());
|
||||
if (cropResultTempFile == null) {
|
||||
ToastUtils.show("裁剪路径创建失败,请重试");
|
||||
return;
|
||||
@@ -595,7 +595,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
ToastUtils.show("无法启动裁剪工具");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算两个整数的最大公约数(GCD,Greatest Common Divisor)
|
||||
* 用途:裁剪时获取视图宽高的最简比例(避免比例值过大导致裁剪工具崩溃)
|
||||
@@ -629,119 +629,119 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
* 保存裁剪后的图片(修复:路径同步时序+压缩图路径强绑定)
|
||||
* 作用:裁剪后保存原图→生成压缩图→同步双路径到预览Bean→清理临时文件
|
||||
*/
|
||||
private void saveCropBitmap(Bitmap bitmap) {
|
||||
LogUtils.d(TAG, "【保存启动】开始保存裁剪图片(仅更新预览Bean,不影响正式Bean)");
|
||||
if (bitmap == null || bitmap.isRecycled()) {
|
||||
ToastUtils.show("裁剪图片为空");
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
// 内存优化:大图片自动缩放(保留原有逻辑,避免OOM)
|
||||
Bitmap scaledBitmap = bitmap;
|
||||
int originalSize = bitmap.getByteCount() / 1024 / 1024;
|
||||
if (originalSize > 5) {
|
||||
float scale = 1.0f;
|
||||
while (scaledBitmap.getByteCount() / 1024 / 1024 > 2) {
|
||||
scale -= 0.2f;
|
||||
if (scale < 0.2f) break;
|
||||
scaledBitmap = scaleBitmap(scaledBitmap, scale); // 复用原有缩放方法
|
||||
}
|
||||
LogUtils.d(TAG, "【内存优化】大图片自动缩放:原始大小=" + originalSize + "MB,缩放后大小=" + scaledBitmap.getByteCount() / 1024 / 1024 + "MB");
|
||||
}
|
||||
|
||||
File cropSaveFile = new File(mBgSourceUtils.getPreviewBackgroundFilePath());
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream bos = null;
|
||||
try {
|
||||
// 1. 清理旧的裁剪预览图(避免文件残留)
|
||||
// if (cropSaveFile.exists()) {
|
||||
// mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图");
|
||||
// private void saveCropBitmap(Bitmap bitmap) {
|
||||
// LogUtils.d(TAG, "【保存启动】开始保存裁剪图片(仅更新预览Bean,不影响正式Bean)");
|
||||
// if (bitmap == null || bitmap.isRecycled()) {
|
||||
// ToastUtils.show("裁剪图片为空");
|
||||
// //mBgSourceUtils.clearCropTempFiles();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 内存优化:大图片自动缩放(保留原有逻辑,避免OOM)
|
||||
// Bitmap scaledBitmap = bitmap;
|
||||
// int originalSize = bitmap.getByteCount() / 1024 / 1024;
|
||||
// if (originalSize > 5) {
|
||||
// float scale = 1.0f;
|
||||
// while (scaledBitmap.getByteCount() / 1024 / 1024 > 2) {
|
||||
// scale -= 0.2f;
|
||||
// if (scale < 0.2f) break;
|
||||
// scaledBitmap = scaleBitmap(scaledBitmap, scale); // 复用原有缩放方法
|
||||
// }
|
||||
// 确保父目录存在(兼容Android 10+分区存储,多包名环境下目录适配)
|
||||
File parentDir = cropSaveFile.getParentFile();
|
||||
if (parentDir != null && !parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
mBgSourceUtils.copyFile(new File(""), parentDir); // 复用原有目录创建逻辑
|
||||
}
|
||||
if (parentDir == null) {
|
||||
LogUtils.e(TAG, "【裁剪保存失败】目标文件父目录为空");
|
||||
ToastUtils.show("裁剪图片保存失败");
|
||||
return;
|
||||
}
|
||||
cropSaveFile.createNewFile();
|
||||
// LogUtils.d(TAG, "【内存优化】大图片自动缩放:原始大小=" + originalSize + "MB,缩放后大小=" + scaledBitmap.getByteCount() / 1024 / 1024 + "MB");
|
||||
// }
|
||||
//
|
||||
// File cropSaveFile = new File(mBgSourceUtils.getPreviewBackgroundFilePath());
|
||||
// FileOutputStream fos = null;
|
||||
// BufferedOutputStream bos = null;
|
||||
// try {
|
||||
// // 1. 清理旧的裁剪预览图(避免文件残留)
|
||||
//// if (cropSaveFile.exists()) {
|
||||
//// mBgSourceUtils.clearOldFileByExternal(cropSaveFile, "旧裁剪预览图");
|
||||
//// }
|
||||
// // 确保父目录存在(兼容Android 10+分区存储,多包名环境下目录适配)
|
||||
// File parentDir = cropSaveFile.getParentFile();
|
||||
// if (parentDir != null && !parentDir.exists()) {
|
||||
// parentDir.mkdirs();
|
||||
// mBgSourceUtils.copyFile(new File(""), parentDir); // 复用原有目录创建逻辑
|
||||
// }
|
||||
// if (parentDir == null) {
|
||||
// LogUtils.e(TAG, "【裁剪保存失败】目标文件父目录为空");
|
||||
// ToastUtils.show("裁剪图片保存失败");
|
||||
// return;
|
||||
// }
|
||||
// cropSaveFile.createNewFile();
|
||||
//
|
||||
// // 2. 写入裁剪原图到指定路径(覆盖旧文件)
|
||||
// fos = new FileOutputStream(cropSaveFile);
|
||||
// bos = new BufferedOutputStream(fos);
|
||||
// scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, bos);
|
||||
// bos.flush();
|
||||
// // 强制同步到磁盘(避免Android 10+异步写入导致文件读取不到)
|
||||
// if (fos != null) {
|
||||
// try {
|
||||
// fos.getFD().sync();
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.w(TAG, "【裁剪保存】sync()调用失败,用flush()兜底:" + e.getMessage());
|
||||
// bos.flush();
|
||||
// }
|
||||
// }
|
||||
// LogUtils.d(TAG, "【裁剪保存】预览原图保存成功:" + cropSaveFile.getAbsolutePath() + ",大小=" + cropSaveFile.length() + "bytes");
|
||||
//
|
||||
// // 3. 生成新压缩图(关键:先获取/生成压缩路径,再压缩)
|
||||
// String newCompressPath = mBgSourceUtils.getPreviewBackgroundScaledCompressFilePath();
|
||||
// // 兜底:若压缩路径为空,生成新的唯一路径(避免空指针,兼容多包名路径隔离)
|
||||
// if (TextUtils.isEmpty(newCompressPath)) {
|
||||
// String sourceDir = mBgSourceUtils.getBackgroundSourceDirPath();
|
||||
// newCompressPath = new File(sourceDir, "ScaledCompress_" + System.currentTimeMillis() + ".jpg").getAbsolutePath();
|
||||
// }
|
||||
// // 调用重载方法压缩,传入指定压缩路径
|
||||
// mBgSourceUtils.compressQualityToRecivedPicture(scaledBitmap, newCompressPath);
|
||||
//
|
||||
// // 4. 同步更新预览Bean(核心修复:同时绑定原图路径+压缩图路径)
|
||||
// mBgSourceUtils.saveFileToPreviewBean(cropSaveFile, "裁剪后图片"); // 原有方法:更新原图路径
|
||||
// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
// if (previewBean != null) {
|
||||
// previewBean.setBackgroundScaledCompressFilePath(newCompressPath); // 新增:绑定压缩图路径
|
||||
// mBgSourceUtils.saveSettings(); // 持久化Bean配置,避免路径丢失(多包名环境下配置隔离)
|
||||
// LogUtils.d(TAG, "【路径同步】预览Bean双路径同步完成:");
|
||||
// LogUtils.d(TAG, "→ 原图路径:" + previewBean.getBackgroundFilePath());
|
||||
// LogUtils.d(TAG, "→ 压缩图路径:" + previewBean.getBackgroundScaledCompressFilePath());
|
||||
// } else {
|
||||
// LogUtils.e(TAG, "【路径同步失败】预览Bean为空");
|
||||
// }
|
||||
//
|
||||
// ToastUtils.show("裁剪图片保存成功");
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "【裁剪保存失败】IO异常:" + e.getMessage(), e);
|
||||
// ToastUtils.show("裁剪图片保存失败");
|
||||
// } finally {
|
||||
// // 资源回收(避免内存泄漏,兼容低版本Android)
|
||||
// if (bos != null) {
|
||||
// try {
|
||||
// bos.close();
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "【流关闭失败】BufferedOutputStream:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// if (fos != null) {
|
||||
// try {
|
||||
// fos.close();
|
||||
// } catch (IOException e) {
|
||||
// LogUtils.e(TAG, "【流关闭失败】FileOutputStream:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// // 回收缩放后的Bitmap(避免重复回收)
|
||||
// if (scaledBitmap != bitmap && scaledBitmap != null && !scaledBitmap.isRecycled()) {
|
||||
// scaledBitmap.recycle();
|
||||
// }
|
||||
// if (bitmap != null && !bitmap.isRecycled()) {
|
||||
// bitmap.recycle();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// LogUtils.d(TAG, "【裁剪保存】流程结束。");
|
||||
// }
|
||||
|
||||
// 2. 写入裁剪原图到指定路径(覆盖旧文件)
|
||||
fos = new FileOutputStream(cropSaveFile);
|
||||
bos = new BufferedOutputStream(fos);
|
||||
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, bos);
|
||||
bos.flush();
|
||||
// 强制同步到磁盘(避免Android 10+异步写入导致文件读取不到)
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.getFD().sync();
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【裁剪保存】sync()调用失败,用flush()兜底:" + e.getMessage());
|
||||
bos.flush();
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【裁剪保存】预览原图保存成功:" + cropSaveFile.getAbsolutePath() + ",大小=" + cropSaveFile.length() + "bytes");
|
||||
|
||||
// 3. 生成新压缩图(关键:先获取/生成压缩路径,再压缩)
|
||||
String newCompressPath = mBgSourceUtils.getPreviewBackgroundScaledCompressFilePath();
|
||||
// 兜底:若压缩路径为空,生成新的唯一路径(避免空指针,兼容多包名路径隔离)
|
||||
if (TextUtils.isEmpty(newCompressPath)) {
|
||||
String sourceDir = mBgSourceUtils.getBackgroundSourceDirPath();
|
||||
newCompressPath = new File(sourceDir, "ScaledCompress_" + System.currentTimeMillis() + ".jpg").getAbsolutePath();
|
||||
}
|
||||
// 调用重载方法压缩,传入指定压缩路径
|
||||
mBgSourceUtils.compressQualityToRecivedPicture(scaledBitmap, newCompressPath);
|
||||
|
||||
// 4. 同步更新预览Bean(核心修复:同时绑定原图路径+压缩图路径)
|
||||
mBgSourceUtils.saveFileToPreviewBean(cropSaveFile, "裁剪后图片"); // 原有方法:更新原图路径
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
if (previewBean != null) {
|
||||
previewBean.setBackgroundScaledCompressFilePath(newCompressPath); // 新增:绑定压缩图路径
|
||||
mBgSourceUtils.saveSettings(); // 持久化Bean配置,避免路径丢失(多包名环境下配置隔离)
|
||||
LogUtils.d(TAG, "【路径同步】预览Bean双路径同步完成:");
|
||||
LogUtils.d(TAG, "→ 原图路径:" + previewBean.getBackgroundFilePath());
|
||||
LogUtils.d(TAG, "→ 压缩图路径:" + previewBean.getBackgroundScaledCompressFilePath());
|
||||
} else {
|
||||
LogUtils.e(TAG, "【路径同步失败】预览Bean为空");
|
||||
}
|
||||
|
||||
ToastUtils.show("裁剪图片保存成功");
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【裁剪保存失败】IO异常:" + e.getMessage(), e);
|
||||
ToastUtils.show("裁剪图片保存失败");
|
||||
} finally {
|
||||
// 资源回收(避免内存泄漏,兼容低版本Android)
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【流关闭失败】BufferedOutputStream:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【流关闭失败】FileOutputStream:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
// 回收缩放后的Bitmap(避免重复回收)
|
||||
if (scaledBitmap != bitmap && scaledBitmap != null && !scaledBitmap.isRecycled()) {
|
||||
scaledBitmap.recycle();
|
||||
}
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "【裁剪保存】流程结束。");
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片缩放(核心用途:大图片自动缩小,降低内存占用,避免OOM崩溃)
|
||||
* 逻辑:按指定缩放比例(0~1之间)缩小Bitmap,保持宽高比不变,生成新的Bitmap并回收旧图
|
||||
@@ -901,11 +901,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
// 同步预览并启动裁剪
|
||||
mBgSourceUtils.saveFileToPreviewBean(mfTakePhoto, mfTakePhoto.getAbsolutePath());
|
||||
doubleRefreshPreview();
|
||||
|
||||
|
||||
startCropImageActivity(false);
|
||||
LogUtils.d(TAG, "【拍照完成】拍照回调处理结束,已启动裁剪");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析拍照结果为Bitmap(核心用途:拍照回调后,从拍照临时文件/Intent中获取Bitmap,用于后续压缩保存)
|
||||
* 逻辑:优先从拍照临时文件解析(清晰原图),失败则从Intent获取缩略图(兜底),双重保障避免解析失败
|
||||
@@ -960,50 +960,65 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
*/
|
||||
private void handleCropImageResult(int requestCode, int resultCode, Intent data) {
|
||||
// 从工具类获取裁剪临时文件(唯一入口)
|
||||
File cropTempFile = mBgSourceUtils.getCropSourceFile();
|
||||
boolean isFileExist = cropTempFile != null && cropTempFile.exists();
|
||||
File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
|
||||
boolean isFileExist = cropTempFile.exists();
|
||||
boolean isFileReadable = isFileExist ? cropTempFile.canRead() : false;
|
||||
long fileSize = isFileExist ? cropTempFile.length() : 0;
|
||||
// 适配MIUI:仅resultCode=RESULT_OK且文件有效时视为成功
|
||||
boolean isCropSuccess = (resultCode == RESULT_OK) && isFileExist && isFileReadable && fileSize > 100;
|
||||
|
||||
if (isCropSuccess) {
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess);
|
||||
mBgSourceUtils.saveSettings();
|
||||
doubleRefreshPreview();
|
||||
} else {
|
||||
// 其他失败场景
|
||||
handleOperationCancelOrFail();
|
||||
}
|
||||
// 打印校验日志
|
||||
LogUtils.d(TAG, "【裁剪回调】校验:resultCode=" + resultCode + ",文件存在=" + isFileExist + ",大小=" + fileSize + "bytes,是否成功=" + isCropSuccess);
|
||||
|
||||
// 处理MIUI裁剪取消(resultCode=0视为取消)
|
||||
if (resultCode == 0 && !isCropSuccess) {
|
||||
LogUtils.d(TAG, "【裁剪回调】MIUI 裁剪工具已取消");
|
||||
ToastUtils.show("裁剪已取消");
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理裁剪文件为空(真正的裁剪失败)
|
||||
if (isFileExist && fileSize == 0) {
|
||||
LogUtils.e(TAG, "【裁剪失败】裁剪文件为空(MIUI适配问题)");
|
||||
ToastUtils.show("裁剪失败,请尝试选择「系统相机」裁剪或更换图片");
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
return;
|
||||
}
|
||||
|
||||
// 裁剪成功:解析Bitmap并保存
|
||||
if (isCropSuccess) {
|
||||
Bitmap cropBitmap = parseCropTempFileToBitmap(cropTempFile);
|
||||
if (cropBitmap != null && !cropBitmap.isRecycled()) {
|
||||
saveCropBitmap(cropBitmap); // 保存裁剪结果
|
||||
doubleRefreshPreview(); // 双重刷新预览(适配MIUI渲染延迟)
|
||||
LogUtils.d(TAG, "【裁剪完成】裁剪回调处理结束");
|
||||
} else {
|
||||
ToastUtils.show("获取裁剪图片失败");
|
||||
LogUtils.e(TAG, "【裁剪回调失败】Bitmap解析异常");
|
||||
//mBgSourceUtils.clearCropTempFiles();
|
||||
}
|
||||
} else {
|
||||
// 其他失败场景
|
||||
handleOperationCancelOrFail();
|
||||
}
|
||||
// LogUtils.d(TAG, "【裁剪回调】校验:resultCode=" + resultCode + ",文件存在=" + isFileExist + ",大小=" + fileSize + "bytes,是否成功=" + isCropSuccess);
|
||||
//
|
||||
// // 处理MIUI裁剪取消(resultCode=0视为取消)
|
||||
// if (resultCode == 0 && !isCropSuccess) {
|
||||
// LogUtils.d(TAG, "【裁剪回调】MIUI 裁剪工具已取消");
|
||||
// ToastUtils.show("裁剪已取消");
|
||||
// //mBgSourceUtils.clearCropTempFiles();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 处理裁剪文件为空(真正的裁剪失败)
|
||||
// if (isFileExist && fileSize == 0) {
|
||||
// LogUtils.e(TAG, "【裁剪失败】裁剪文件为空(MIUI适配问题)");
|
||||
// ToastUtils.show("裁剪失败,请尝试选择「系统相机」裁剪或更换图片");
|
||||
// //mBgSourceUtils.clearCropTempFiles();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 裁剪成功:解析Bitmap并保存
|
||||
// if (isCropSuccess) {
|
||||
// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
// previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess);
|
||||
// mBgSourceUtils.saveSettings();
|
||||
//
|
||||
// doubleRefreshPreview();
|
||||
//
|
||||
// Bitmap cropBitmap = parseCropTempFileToBitmap(cropTempFile);
|
||||
// if (cropBitmap != null && !cropBitmap.isRecycled()) {
|
||||
// saveCropBitmap(cropBitmap); // 保存裁剪结果
|
||||
// // 更新预览数据集设置
|
||||
// BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
// previewBean.setIsUseBackgroundScaledCompressFile(isCropSuccess);
|
||||
// mBgSourceUtils.saveSettings();
|
||||
//
|
||||
// doubleRefreshPreview(); // 双重刷新预览(适配MIUI渲染延迟)
|
||||
// LogUtils.d(TAG, "【裁剪完成】裁剪回调处理结束");
|
||||
// } else {
|
||||
// ToastUtils.show("获取裁剪图片失败");
|
||||
// LogUtils.e(TAG, "【裁剪回调失败】Bitmap解析异常");
|
||||
// //mBgSourceUtils.clearCropTempFiles();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析裁剪临时文件为Bitmap(核心用途:裁剪回调后,将裁剪生成的临时文件转为Bitmap,用于后续保存/预览)
|
||||
* 逻辑:采用采样率加载(避免OOM),支持JPEG/PNG等主流格式,兼容破损文件/空文件场景
|
||||
@@ -1081,7 +1096,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
return cropBitmap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 双重刷新预览(核心用途:适配MIUI等机型裁剪/选图后预览渲染延迟,解决“图片已保存但界面空白”问题)
|
||||
* 逻辑:先立即刷新一次,再延迟短时间二次刷新,确保控件能获取到最新的图片文件(规避异步渲染/文件写入延迟)
|
||||
@@ -1134,78 +1149,61 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
// 关键修复1:解析Uri为文件(强制使用独立的选图临时文件,避免路径混淆)
|
||||
mBgSourceUtils.createCropFileProviderPath(selectedImage);
|
||||
if (mBgSourceUtils.getCropSourceFile() == null || !mBgSourceUtils.getCropSourceFile().exists() || mBgSourceUtils.getCropSourceFile().length() <= 0) {
|
||||
ToastUtils.show("选择的图片文件无效或无法读取");
|
||||
return;
|
||||
}
|
||||
LogUtils.d(TAG, "【选图解析】选图临时文件生成成功:" + mBgSourceUtils.getCropSourceFile().getAbsolutePath() + ",大小:" + mBgSourceUtils.getCropSourceFile().length() + "bytes");
|
||||
|
||||
// 关键修复2:同步预览Bean(强制绑定「原图路径+压缩图路径」双路径,避免路径错位)
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
if (previewBean == null) {
|
||||
ToastUtils.show("预览配置初始化失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 关键:强制绑定压缩图路径到BackgroundCrops目录(统一存储,避免路径错乱)
|
||||
String targetCompressPath = mBgSourceUtils.getCropResultFile().getAbsolutePath();
|
||||
|
||||
String targetCompressPath = mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath();
|
||||
|
||||
LogUtils.d(TAG, "【选图同步】预览Bean双路径绑定完成:");
|
||||
LogUtils.d(TAG, "→ 原图路径(选图临时文件):" + previewBean.getBackgroundFilePath());
|
||||
LogUtils.d(TAG, "→ 压缩图路径(BackgroundCrops):" + previewBean.getBackgroundScaledCompressFilePath());
|
||||
LogUtils.d(TAG, "→ 原图路径(选图临时文件):" + mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath());
|
||||
LogUtils.d(TAG, "→ 压缩图路径 :" + targetCompressPath);
|
||||
|
||||
startCropImageActivity(false);
|
||||
|
||||
// 关键修复3:生成压缩图(指定BackgroundCrops目录下的路径,确保与预览Bean路径一致)
|
||||
LogUtils.d(TAG, "【选图压缩】开始生成预览压缩图,目标路径:" + targetCompressPath);
|
||||
Bitmap selectBitmap = null;
|
||||
try {
|
||||
// 优化:使用采样率加载Bitmap,避免OOM(原有直接decodeFile易导致大图片崩溃)
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options);
|
||||
// 计算采样率(最大尺寸限制为2048px)
|
||||
int maxSize = 2048;
|
||||
int sampleSize = 1;
|
||||
while (options.outWidth / sampleSize > maxSize || options.outHeight / sampleSize > maxSize) {
|
||||
sampleSize *= 2;
|
||||
}
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存
|
||||
selectBitmap = BitmapFactory.decodeFile(mBgSourceUtils.getCropSourceFile().getAbsolutePath(), options);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【选图压缩】Bitmap加载失败:" + e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (selectBitmap != null && !selectBitmap.isRecycled()) {
|
||||
mBgSourceUtils.compressQualityToRecivedPicture(selectBitmap, targetCompressPath); // 调用重载函数,指定路径压缩
|
||||
selectBitmap.recycle(); // 及时回收,避免内存泄漏
|
||||
} else {
|
||||
ToastUtils.show("选图后压缩图生成失败");
|
||||
LogUtils.e(TAG, "【选图压缩失败】无法解析选图文件为Bitmap");
|
||||
return;
|
||||
}
|
||||
//LogUtils.d(TAG, "【选图压缩】开始生成预览压缩图,目标路径:" + targetCompressPath);
|
||||
// Bitmap selectBitmap = null;
|
||||
// try {
|
||||
// // 优化:使用采样率加载Bitmap,避免OOM(原有直接decodeFile易导致大图片崩溃)
|
||||
// BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
// options.inJustDecodeBounds = true;
|
||||
// BitmapFactory.decodeFile(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath(), options);
|
||||
// // 计算采样率(最大尺寸限制为2048px)
|
||||
// int maxSize = 2048;
|
||||
// int sampleSize = 1;
|
||||
// while (options.outWidth / sampleSize > maxSize || options.outHeight / sampleSize > maxSize) {
|
||||
// sampleSize *= 2;
|
||||
// }
|
||||
// options.inJustDecodeBounds = false;
|
||||
// options.inSampleSize = sampleSize;
|
||||
// options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存
|
||||
// selectBitmap = BitmapFactory.decodeFile(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundFilePath(), options);
|
||||
// LogUtils.d(TAG, "");
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "【选图压缩】Bitmap加载失败:" + e.getMessage(), e);
|
||||
// }
|
||||
|
||||
// 关键修复4:校验压缩图是否生成成功(避免压缩失败但仍启动裁剪)
|
||||
File compressFile = new File(targetCompressPath);
|
||||
if (!compressFile.exists() || compressFile.length() <= 0) {
|
||||
ToastUtils.show("压缩图生成失败,请重新选择图片");
|
||||
LogUtils.e(TAG, "【选图压缩失败】压缩图文件无效:" + targetCompressPath);
|
||||
return;
|
||||
}
|
||||
// File compressFile = new File(targetCompressPath);
|
||||
// if (!compressFile.exists() || compressFile.length() <= 0) {
|
||||
// ToastUtils.show("压缩图生成失败,请重新选择图片");
|
||||
// LogUtils.e(TAG, "【选图压缩失败】压缩图文件无效:" + targetCompressPath);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 刷新预览并启动裁剪(无需重复同步预览Bean,已提前绑定)
|
||||
doubleRefreshPreview();
|
||||
|
||||
// 新增:延迟50ms启动裁剪(适配部分机型选图后文件写入延迟)
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isFinishing()) {
|
||||
startCropImageActivity(false);
|
||||
LogUtils.d(TAG, "【选图完成】选图回调处理结束,已启动裁剪(正式图+压缩图均已生成)");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
/*doubleRefreshPreview();
|
||||
|
||||
// 新增:延迟50ms启动裁剪(适配部分机型选图后文件写入延迟)
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isFinishing()) {
|
||||
startCropImageActivity(false);
|
||||
LogUtils.d(TAG, "【选图完成】选图回调处理结束,已启动裁剪(正式图+压缩图均已生成)");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1214,7 +1212,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
private void handleOperationCancelOrFail() {
|
||||
initBackgroundViewByPreviewBean();
|
||||
doubleRefreshPreview();
|
||||
|
||||
|
||||
LogUtils.d(TAG, "【操作回调】操作取消或失败");
|
||||
ToastUtils.show("操作已取消");
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ public class BackgroundSourceUtils {
|
||||
*/
|
||||
private void initAllFiles() {
|
||||
// 1. 裁剪临时文件
|
||||
mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME);
|
||||
//mCropSourceFile = new File(fCropCacheDir, CROP_TEMP_FILE_NAME);
|
||||
// 2. 裁剪结果文件
|
||||
//cropResultFile = new File(fCropCacheDir, CROP_RESULT_FILE_NAME);
|
||||
|
||||
@@ -216,18 +216,32 @@ public class BackgroundSourceUtils {
|
||||
clearCropTempFiles();
|
||||
LogUtils.d(TAG, "【文件初始化】完成。");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean createCropFileProviderPath(Uri uri) {
|
||||
InputStream is = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
|
||||
try {
|
||||
clearCropTempFiles();
|
||||
String szType = mContext.getContentResolver().getType(uri);
|
||||
// 2. 截取MIME类型后缀(如从image/jpeg中提取jpeg)【核心新增逻辑】
|
||||
String fileSuffix = "";
|
||||
if (szType != null && szType.contains("/")) {
|
||||
// 分割字符串,取"/"后面的部分(如"image/jpeg" → 分割后取索引1的"jpeg")
|
||||
fileSuffix = szType.split("/")[1];
|
||||
// 调试日志:打印截取后的文件后缀
|
||||
} else {
|
||||
// 异常处理:若类型为空或格式错误,默认后缀设为jpeg(保留原逻辑兼容性)
|
||||
fileSuffix = "jpeg";
|
||||
}
|
||||
String newCropFileName = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||
mCropSourceFile = new File(fCropCacheDir, newCropFileName + ".jpg");
|
||||
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + ".jpg");
|
||||
|
||||
mCropSourceFile = new File(fCropCacheDir, newCropFileName + "." + fileSuffix);
|
||||
mCropResultFile = new File(fCropCacheDir, "SelectCompress_" + newCropFileName + "." + fileSuffix);
|
||||
|
||||
mCropSourceFile.createNewFile();
|
||||
mCropResultFile.createNewFile();
|
||||
|
||||
// 1. 打开Uri输入流(兼容content:///file:// 等多种Uri格式)
|
||||
is = mContext.getContentResolver().openInputStream(uri);
|
||||
if (is == null) {
|
||||
@@ -255,13 +269,13 @@ public class BackgroundSourceUtils {
|
||||
fos.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
previewBackgroundBean.setBackgroundFileName(mCropSourceFile.getName());
|
||||
previewBackgroundBean.setBackgroundFilePath(mCropSourceFile.getAbsolutePath());
|
||||
|
||||
|
||||
previewBackgroundBean.setBackgroundScaledCompressFileName(mCropResultFile.getName());
|
||||
previewBackgroundBean.setBackgroundScaledCompressFilePath(mCropResultFile.getAbsolutePath());
|
||||
|
||||
|
||||
// 6. 解析成功日志(打印文件信息,便于问题排查)
|
||||
LogUtils.d(TAG, "【选图解析】Uri解析成功!");
|
||||
LogUtils.d(TAG, "→ 原Uri:" + uri.toString());
|
||||
@@ -458,14 +472,6 @@ public class BackgroundSourceUtils {
|
||||
return fBackgroundCompressDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
public File getCropSourceFile() {
|
||||
return mCropSourceFile;
|
||||
}
|
||||
|
||||
public File getCropResultFile() {
|
||||
return mCropResultFile;
|
||||
}
|
||||
|
||||
public String getFileProviderAuthority() {
|
||||
return FILE_PROVIDER_AUTHORITY;
|
||||
}
|
||||
@@ -654,7 +660,7 @@ public class BackgroundSourceUtils {
|
||||
* 新增:清理压缩图目录下的旧文件(避免残留,初始化时调用)
|
||||
*/
|
||||
void clearCropTempFiles() {
|
||||
for(File file : fCropCacheDir.listFiles()){
|
||||
for (File file : fCropCacheDir.listFiles()) {
|
||||
clearOldFile(file, "旧裁剪缓存文件(" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
mCropSourceFile = null;
|
||||
@@ -881,6 +887,7 @@ public class BackgroundSourceUtils {
|
||||
*/
|
||||
public void compressQualityToRecivedPicture(Bitmap bitmap, String targetCompressPath) {
|
||||
LogUtils.d(TAG, "【压缩启动】开始压缩图片(指定路径),Bitmap状态:" + (bitmap != null && !bitmap.isRecycled()));
|
||||
|
||||
if (bitmap == null || bitmap.isRecycled()) {
|
||||
ToastUtils.show("压缩失败:图片为空");
|
||||
LogUtils.e(TAG, "【压缩失败】Bitmap为空或已回收");
|
||||
@@ -890,48 +897,16 @@ public class BackgroundSourceUtils {
|
||||
OutputStream outStream = null;
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
String scaledCompressFilePath = targetCompressPath;
|
||||
// 兜底:若传入路径为空,生成BackgroundCrops目录下的临时压缩路径(兼容异常场景)
|
||||
if (TextUtils.isEmpty(scaledCompressFilePath)) {
|
||||
LogUtils.e(TAG, "【压缩异常】指定路径为空,使用BackgroundCrops临时兜底路径");
|
||||
// 强制在BackgroundCrops目录下生成临时路径(不再用其他目录)
|
||||
String tempCompressName = "preview_compress_" + System.currentTimeMillis() + ".jpg";
|
||||
scaledCompressFilePath = new File(fBackgroundCompressDir, tempCompressName).getAbsolutePath();
|
||||
LogUtils.d(TAG, "【压缩兜底】BackgroundCrops临时路径:" + scaledCompressFilePath);
|
||||
} else {
|
||||
// 强制校验:传入路径必须在BackgroundCrops目录下,否则重置(确保统一存储)
|
||||
if (!scaledCompressFilePath.contains(fBackgroundCompressDir.getAbsolutePath())) {
|
||||
LogUtils.w(TAG, "【压缩校验】传入路径不在BackgroundCrops目录,自动重置");
|
||||
String compressFileName = new File(scaledCompressFilePath).getName();
|
||||
scaledCompressFilePath = new File(fBackgroundCompressDir, compressFileName).getAbsolutePath();
|
||||
LogUtils.d(TAG, "【压缩校验】重置后路径:" + scaledCompressFilePath);
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【压缩配置】目标路径(BackgroundCrops):" + targetCompressPath + ",Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB");
|
||||
|
||||
File compressFile = new File(scaledCompressFilePath);
|
||||
LogUtils.d(TAG, "【压缩配置】目标路径(BackgroundCrops):" + scaledCompressFilePath + ",Bitmap原始大小:" + bitmap.getByteCount() / 1024 + "KB");
|
||||
|
||||
// 确保压缩图目录存在(实例化时已创建,此处二次确认)
|
||||
File parentDir = compressFile.getParentFile();
|
||||
if (parentDir == null) {
|
||||
LogUtils.e(TAG, "【压缩异常】父目录为空,无法创建文件");
|
||||
ToastUtils.show("压缩失败:路径无效");
|
||||
return;
|
||||
File targetCompressFile = new File(targetCompressPath);
|
||||
if (targetCompressFile.exists()) {
|
||||
targetCompressFile.delete();
|
||||
}
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
FileUtils.copyFile(new File(""), parentDir);
|
||||
LogUtils.d(TAG, "【压缩准备】BackgroundCrops目录已创建:" + parentDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 清理旧的压缩文件(避免文件残留)
|
||||
// if (compressFile.exists()) {
|
||||
// clearOldFileByExternal(compressFile, "旧压缩文件(BackgroundCrops)");
|
||||
// }
|
||||
compressFile.createNewFile();
|
||||
targetCompressFile.createNewFile();
|
||||
|
||||
// 写入压缩图(质量80,平衡清晰度和内存)
|
||||
fos = new FileOutputStream(compressFile);
|
||||
fos = new FileOutputStream(targetCompressFile);
|
||||
outStream = new BufferedOutputStream(fos);
|
||||
boolean compressSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
|
||||
outStream.flush();
|
||||
@@ -946,21 +921,13 @@ public class BackgroundSourceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "【压缩结果】" + (compressSuccess ? "成功" : "失败") + ",大小:" + compressFile.length() / 1024 + "KB,路径:" + scaledCompressFilePath);
|
||||
LogUtils.d(TAG, "【压缩结果】" + (compressSuccess ? "成功" : "失败") + ",大小:" + targetCompressFile.length() / 1024 + "KB,路径:" + targetCompressFile);
|
||||
|
||||
// 关键修复:压缩成功后,强制同步路径到预览Bean(双重保障,避免时序错位)
|
||||
if (compressSuccess) {
|
||||
BackgroundBean previewBean = getPreviewBackgroundBean();
|
||||
if (previewBean != null) {
|
||||
previewBean.setBackgroundScaledCompressFilePath(scaledCompressFilePath);
|
||||
saveSettings(); // 持久化配置,多包名环境下配置隔离
|
||||
LogUtils.d(TAG, "【压缩路径同步】已绑定到预览Bean(BackgroundCrops):" + scaledCompressFilePath);
|
||||
} else {
|
||||
LogUtils.e(TAG, "【压缩路径同步失败】预览Bean为空");
|
||||
}
|
||||
ToastUtils.show("图片压缩成功");
|
||||
} else {
|
||||
ToastUtils.show("图片压缩失败");
|
||||
LogUtils.e(TAG, "【压缩失败】Bitmap.compress()返回false");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【压缩异常】IO错误:" + e.getMessage(), e);
|
||||
|
||||
Reference in New Issue
Block a user