20251201_004105_116
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Sun Nov 30 18:48:20 HKT 2025
|
||||
#Sun Nov 30 16:39:04 GMT 2025
|
||||
stageCount=13
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=0
|
||||
buildCount=4
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -52,46 +52,42 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
private File mfBackgroundDir; // 背景图片存储文件夹
|
||||
private File mfPictureDir; // 拍照与剪裁临时文件夹
|
||||
private File mfTakePhoto; // 拍照文件
|
||||
//private File mfRecivedPicture; // 接收的图片文件
|
||||
|
||||
// 背景视图预览图片的文件名
|
||||
private String preViewFilePath = "";
|
||||
private String preViewFileUrl = "";
|
||||
BackgroundView bvPreviewBackground;
|
||||
boolean isCommitSettings = false;
|
||||
// 背景视图预览图片的文件名
|
||||
private String preViewFilePath = "";
|
||||
private String preViewFileUrl = "";
|
||||
BackgroundView bvPreviewBackground;
|
||||
boolean isCommitSettings = false;
|
||||
|
||||
// 静态变量
|
||||
// 源文件的临时剪裁图片保存名称
|
||||
// 源文件的临时剪裁图片保存名称
|
||||
private static String _mSourceCropTempFileName = "SourceCropTemp.jpg";
|
||||
// 源文件的临时剪裁图片保存文件对象
|
||||
private static File _mSourceCropTempFile;
|
||||
// 源文件的临时剪裁图片保存文件对象
|
||||
private static File _mSourceCropTempFile;
|
||||
// 源文件的剪裁图片保存名称
|
||||
private static String _mSourceCroppedFileName = "SourceCropped.jpg";
|
||||
// 源文件的剪裁图片保存文件对象
|
||||
private static File _mSourceCroppedFile;
|
||||
// 源文件的剪裁图片保存路径
|
||||
private static String _mSourceCroppedFilePath;
|
||||
// 源文件的剪裁图片保存文件对象
|
||||
private static File _mSourceCroppedFile;
|
||||
// 源文件的剪裁图片保存路径
|
||||
private static String _mSourceCroppedFilePath;
|
||||
private static String _mszCommonFileType = "jpeg";
|
||||
private int mnPictureCompress = 100;
|
||||
//private static String _RecivedBackgroundFileName;
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_backgroundpicture);
|
||||
bvPreviewBackground = (BackgroundView) findViewById(R.id.activitybackgroundpictureBackgroundView1);
|
||||
|
||||
//initEnv();
|
||||
bvPreviewBackground = (BackgroundView) findViewById(R.id.activitybackgroundpictureBackgroundView1);
|
||||
|
||||
// 初始化工具类和文件夹
|
||||
mBackgroundSourceUtils = BackgroundSourceUtils.getInstance(this);
|
||||
@@ -107,13 +103,10 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
// 初始化文件对象
|
||||
mfTakePhoto = new File(mfPictureDir, "TakePhoto.jpg");
|
||||
//mfTempCropPicture = new File(mfPictureDir, "TempCrop.jpg");
|
||||
|
||||
//mfRecivedPicture = getRecivedPictureFile();
|
||||
_mSourceCropTempFile = new File(mfBackgroundDir, _mSourceCropTempFileName);
|
||||
_mSourceCroppedFile = new File(mfBackgroundDir, _mSourceCroppedFileName);
|
||||
_mSourceCroppedFilePath = _mSourceCroppedFile.getAbsolutePath().toString();
|
||||
LogUtils.d(TAG, String.format("_mSourceCroppedFilePath : %s", _mSourceCroppedFilePath));
|
||||
_mSourceCroppedFilePath = _mSourceCroppedFile.getAbsolutePath().toString();
|
||||
LogUtils.d(TAG, String.format("_mSourceCroppedFilePath : %s", _mSourceCroppedFilePath));
|
||||
|
||||
// 初始化工具栏
|
||||
mAToolbar = (AToolbar) findViewById(R.id.toolbar);
|
||||
@@ -121,11 +114,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mAToolbar.setSubtitle(R.string.subtitle_activity_backgroundpicture);
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
mAToolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish(); // 点击导航栏返回按钮,触发 finish()
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish(); // 点击导航栏返回按钮,触发 finish()
|
||||
}
|
||||
});
|
||||
|
||||
// 设置按钮点击事件
|
||||
findViewById(R.id.activitybackgroundpictureAButton5).setOnClickListener(onOriginNullClickListener);
|
||||
@@ -134,12 +127,13 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
findViewById(R.id.activitybackgroundpictureAButton2).setOnClickListener(onSelectPictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton3).setOnClickListener(onCropPictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton6).setOnClickListener(onCropFreePictureClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton7).setOnClickListener(onPixelPickerClickListener);
|
||||
findViewById(R.id.activitybackgroundpictureAButton8).setOnClickListener(onCleanPixelClickListener);
|
||||
|
||||
// 初始预览:加载当前背景到预览并刷新视图
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
utils.setCurrentSourceToPreview();
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
utils.setCurrentSourceToPreview();
|
||||
bvPreviewBackground.reloadPreviewBackground(); // 修复:调用预览刷新方法
|
||||
|
||||
// 处理分享的图片
|
||||
Intent intent = getIntent();
|
||||
@@ -152,74 +146,28 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
|
||||
// private void initEnv() {
|
||||
// LogUtils.d(TAG, "initEnv()");
|
||||
// _RecivedBackgroundFileName = "SourcePicture.data";
|
||||
// }
|
||||
|
||||
public static String getBackgroundFileName() {
|
||||
return _mSourceCroppedFileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAcceptRecivedPicture(String szPreRecivedPictureName) {
|
||||
ToastUtils.show("onAcceptRecivedPicture not yet.");
|
||||
// BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
// utils.getCurrentBackgroundBean().setIsUseBackgroundFile(true);
|
||||
// utils.saveSettings();
|
||||
//
|
||||
// File sourceFile = new File(utils.getBackgroundSourceDirPath(), szPreRecivedPictureName);
|
||||
// if (FileUtils.copyFile(sourceFile, mfRecivedPicture)) {
|
||||
// startCropImageActivity(false);
|
||||
// } else {
|
||||
// ToastUtils.show("图片复制失败,请重试");
|
||||
// }
|
||||
ToastUtils.show("onAcceptRecivedPicture not yet.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新背景图片预览,
|
||||
* 如果sourceFile参数为空,则加载旧的背景图片资源
|
||||
* 更新背景图片预览
|
||||
* 如果sourceFile参数为空,则加载旧的背景图片资源
|
||||
*/
|
||||
public void updateBackgroundView(File sourceFile, String sourceFileInfo) {
|
||||
LogUtils.d(TAG, "updatePreviewBackground");
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
if (sourceFile == null) {
|
||||
bvPreviewBackground.reloadCurrentBackground();
|
||||
} else {
|
||||
utils.saveFileToPreviewBean(sourceFile, sourceFileInfo);
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
}
|
||||
|
||||
// boolean isUseBackgroundFile = utils.getCurrentBackgroundBean().isUseBackgroundFile();
|
||||
// LogUtils.d(TAG, String.format("isUseBackgroundFile is %s, _mSourceCroppedFile.exists() is %s ", isUseBackgroundFile, _mSourceCroppedFile.exists()));
|
||||
//
|
||||
// //if (isUseBackgroundFile && _mSourceCroppedFile.exists()) {
|
||||
// if (_mSourceCroppedFile.exists()) {
|
||||
// //try {
|
||||
// //String filePath = utils.getBackgroundDir() + getBackgroundFileName();
|
||||
// preViewFilePath = _mSourceCroppedFilePath;
|
||||
// LogUtils.d(TAG, String.format("preViewFilePathBackgroundView : %s", preViewFilePath));
|
||||
// bvPreviewBackground.previewBackgroundImage(preViewFilePath);
|
||||
// /*Drawable drawable = FileUtils.getImageDrawable(filePath);
|
||||
// if (drawable != null) {
|
||||
// //drawable.setAlpha(120);
|
||||
// //bvPreviewBackground.setImageDrawable(drawable);
|
||||
// }*/
|
||||
// //ToastUtils.show("背景图片已更新");
|
||||
//// } catch (IOException e) {
|
||||
//// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
//// ToastUtils.show("背景图片加载失败");
|
||||
//// }
|
||||
// } else {
|
||||
// ToastUtils.show("未使用背景图片");
|
||||
// preViewFilePath = "";
|
||||
// bvPreviewBackground.previewBackgroundImage(preViewFilePath);
|
||||
//// Drawable drawable = getResources().getDrawable(R.drawable.blank10x10);
|
||||
//// if (drawable != null) {
|
||||
//// drawable.setAlpha(120);
|
||||
//// bvPreviewBackground.setImageDrawable(drawable);
|
||||
//// }
|
||||
// }
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
if (sourceFile == null) {
|
||||
bvPreviewBackground.reloadCurrentBackground(); // 修复:调用正式背景刷新
|
||||
} else {
|
||||
utils.saveFileToPreviewBean(sourceFile, sourceFileInfo);
|
||||
bvPreviewBackground.reloadPreviewBackground(); // 修复:调用预览背景刷新
|
||||
}
|
||||
}
|
||||
|
||||
// 点击事件监听器
|
||||
@@ -230,10 +178,11 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
bean.setIsUseBackgroundFile(false);
|
||||
utils.saveSettings();
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
bvPreviewBackground.reloadPreviewBackground(); // 修复:刷新预览
|
||||
}
|
||||
};
|
||||
|
||||
// 修复:选择图片后添加视图刷新逻辑
|
||||
private View.OnClickListener onSelectPictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -295,35 +244,29 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
private View.OnClickListener onReceivedPictureClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ToastUtils.show("onReceivedPictureClickListener not yet.");
|
||||
// BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
// utils.getCurrentBackgroundBean().setIsUseBackgroundFile(true);
|
||||
// utils.saveSettings();
|
||||
// updateBackgroundView();
|
||||
ToastUtils.show("onReceivedPictureClickListener not yet.");
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onPixelPickerClickListener = new View.OnClickListener() {
|
||||
private View.OnClickListener onPixelPickerClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 从文件路径启动像素拾取活动
|
||||
//String imagePath = "/storage/emulated/0/DCIM/Camera/sample.jpg";
|
||||
String imagePath = _mSourceCroppedFile.toString();
|
||||
Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class);
|
||||
intent.putExtra("imagePath", imagePath);
|
||||
startActivity(intent);
|
||||
//App.getWinBoLLActivityManager().startWinBoLLActivity(getActivity(), intent, PixelPickerActivity.class);
|
||||
// 从文件路径启动像素拾取活动
|
||||
String imagePath = _mSourceCroppedFile.toString();
|
||||
Intent intent = new Intent(getApplicationContext(), PixelPickerActivity.class);
|
||||
intent.putExtra("imagePath", imagePath);
|
||||
startActivity(intent);
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() {
|
||||
private View.OnClickListener onCleanPixelClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
bean.setPixelColor(0);
|
||||
utils.saveSettings();
|
||||
setBackgroundColor();
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
bean.setPixelColor(0);
|
||||
utils.saveSettings();
|
||||
setBackgroundColor();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -333,7 +276,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
void compressQualityToRecivedPicture(Bitmap bitmap) {
|
||||
OutputStream outStream = null;
|
||||
try {
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
File fRecivedPicture = new File(utils.getPreviewBackgroundScaledCompressFilePath());
|
||||
if (!fRecivedPicture.exists()) {
|
||||
fRecivedPicture.createNewFile();
|
||||
@@ -361,18 +304,18 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动图片裁剪活动
|
||||
* 启动图片裁剪活动(核心修复:传递BackgroundView实际宽高比例,替代Bean默认值)
|
||||
* @param isCropFree 是否自由裁剪
|
||||
*/
|
||||
public void startCropImageActivity(boolean isCropFree) {
|
||||
LogUtils.d(TAG, "startCropImageActivity");
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
BackgroundBean bean = utils.getPreviewBackgroundBean();
|
||||
bean.setIsUseScaledCompress(true);
|
||||
utils.saveSettings();
|
||||
|
||||
File fRecivedPicture = new File(utils.getPreviewBackgroundFilePath());
|
||||
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
BackgroundBean bean = utils.getPreviewBackgroundBean();
|
||||
bean.setIsUseScaledCompress(true);
|
||||
utils.saveSettings();
|
||||
|
||||
File fRecivedPicture = new File(utils.getPreviewBackgroundFilePath());
|
||||
|
||||
Uri uri = UriUtil.getUriForFile(this, fRecivedPicture);
|
||||
LogUtils.d(TAG, "uri : " + uri.toString());
|
||||
|
||||
@@ -396,9 +339,28 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
intent.putExtra("crop", "true");
|
||||
intent.putExtra("noFaceDetection", true);
|
||||
|
||||
// 修复核心:非自由裁剪时,传递bvPreviewBackground控件的实际宽高比例(确保裁剪与控件匹配)
|
||||
if (!isCropFree) {
|
||||
intent.putExtra("aspectX", bean.getBackgroundWidth());
|
||||
intent.putExtra("aspectY", bean.getBackgroundHeight());
|
||||
// 1. 优先获取BackgroundView的实际宽高(控件已填充父视图,宽高=父容器尺寸)
|
||||
int viewWidth = bvPreviewBackground.getWidth();
|
||||
int viewHeight = bvPreviewBackground.getHeight();
|
||||
|
||||
// 2. 容错处理:若控件未测量完成(宽/高为0),使用屏幕尺寸兜底
|
||||
if (viewWidth <= 0 || viewHeight <= 0) {
|
||||
viewWidth = getResources().getDisplayMetrics().widthPixels;
|
||||
viewHeight = getResources().getDisplayMetrics().heightPixels;
|
||||
LogUtils.d(TAG, "控件未测量完成,使用屏幕尺寸作为裁剪比例:" + viewWidth + "x" + viewHeight);
|
||||
}
|
||||
|
||||
// 3. 计算宽高比的最大公约数,简化比例(避免过大数值导致裁剪工具不兼容,如1080:1920→9:16)
|
||||
int gcd = calculateGCD(viewWidth, viewHeight);
|
||||
int simplifiedWidth = viewWidth / gcd;
|
||||
int simplifiedHeight = viewHeight / gcd;
|
||||
|
||||
// 4. 传递简化后的宽高比例给裁剪意图(关键:确保裁剪比例与控件完全匹配)
|
||||
intent.putExtra("aspectX", simplifiedWidth);
|
||||
intent.putExtra("aspectY", simplifiedHeight);
|
||||
LogUtils.d(TAG, "裁剪比例(控件实际比例/简化后):" + viewWidth + ":" + viewHeight + " → " + simplifiedWidth + ":" + simplifiedHeight);
|
||||
}
|
||||
|
||||
intent.putExtra("return-data", true);
|
||||
@@ -411,84 +373,134 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存剪裁后的Bitmap(优化版)
|
||||
* 工具方法:计算两个数的最大公约数(用于简化宽高比)
|
||||
* @param a 第一个数(宽)
|
||||
* @param b 第二个数(高)
|
||||
* @return 最大公约数
|
||||
*/
|
||||
private void saveCropBitmap(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
ToastUtils.show("剪裁图片为空");
|
||||
return;
|
||||
private int calculateGCD(int a, int b) {
|
||||
if (b == 0) {
|
||||
return a;
|
||||
}
|
||||
return calculateGCD(b, a % b);
|
||||
}
|
||||
|
||||
// 内存优化:大图片自动缩放
|
||||
Bitmap scaledBitmap = bitmap;
|
||||
if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB
|
||||
float scale = 1.0f;
|
||||
while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) {
|
||||
scale -= 0.2f; // 每次缩小20%
|
||||
if (scale < 0.2f) break; // 最小缩放到20%
|
||||
scaledBitmap = scaleBitmap(scaledBitmap, scale);
|
||||
}
|
||||
if (scaledBitmap != bitmap) {
|
||||
bitmap.recycle(); // 回收原Bitmap
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 保存剪裁后的Bitmap(优化版,修复裁剪后加载不到图片问题)
|
||||
*/
|
||||
private void saveCropBitmap(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
ToastUtils.show("剪裁图片为空");
|
||||
// 修复:临时文件异常时也清理
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "裁剪图片为空,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 优化:创建保存目录
|
||||
File backgroundDir = new File(mBackgroundSourceUtils.getBackgroundSourceDirPath());
|
||||
if (!backgroundDir.exists()) {
|
||||
if (!backgroundDir.mkdirs()) {
|
||||
ToastUtils.show("无法创建保存目录");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 内存优化:大图片自动缩放(保持原逻辑)
|
||||
Bitmap scaledBitmap = bitmap;
|
||||
if (bitmap.getByteCount() > 10 * 1024 * 1024) { // 超过10MB
|
||||
float scale = 1.0f;
|
||||
while (scaledBitmap.getByteCount() > 5 * 1024 * 1024) {
|
||||
scale -= 0.2f; // 每次缩小20%
|
||||
if (scale < 0.2f) break; // 最小缩放到20%
|
||||
scaledBitmap = scaleBitmap(scaledBitmap, scale);
|
||||
}
|
||||
if (scaledBitmap != bitmap) {
|
||||
bitmap.recycle(); // 回收原Bitmap
|
||||
}
|
||||
}
|
||||
|
||||
// 剪裁的图片的保存地址
|
||||
File fScaledCompressBitmapFile = new File(backgroundDir, BackgroundSourceUtils.getInstance(this).getPreviewBackgroundScaledCompressFilePath());
|
||||
// 优化:创建保存目录(保持原逻辑)
|
||||
File backgroundDir = new File(mBackgroundSourceUtils.getBackgroundSourceDirPath());
|
||||
if (!backgroundDir.exists()) {
|
||||
if (!backgroundDir.mkdirs()) {
|
||||
ToastUtils.show("无法创建保存目录");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 优化:检查文件是否可写
|
||||
if (fScaledCompressBitmapFile.exists() && !fScaledCompressBitmapFile.canWrite()) {
|
||||
if (!fScaledCompressBitmapFile.delete()) {
|
||||
ToastUtils.show("无法删除旧文件");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 剪裁的图片的保存地址(保持原逻辑)
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
String scaledCompressFileName = utils.getPreviewBackgroundScaledCompressFilePath();
|
||||
File fScaledCompressBitmapFile = new File(scaledCompressFileName);
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(fScaledCompressBitmapFile);
|
||||
boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
|
||||
fos.flush();
|
||||
if (success) {
|
||||
// 优化:检查文件是否可写(保持原逻辑)
|
||||
if (fScaledCompressBitmapFile.exists() && !fScaledCompressBitmapFile.canWrite()) {
|
||||
if (!fScaledCompressBitmapFile.delete()) {
|
||||
ToastUtils.show("无法删除旧文件");
|
||||
if (scaledBitmap != bitmap) scaledBitmap.recycle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(fScaledCompressBitmapFile);
|
||||
boolean success = scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
|
||||
fos.flush();
|
||||
if (success) {
|
||||
ToastUtils.show("图片压缩保存成功");
|
||||
BackgroundSourceUtils.getInstance(this).getPreviewBackgroundBean().setIsUseScaledCompress(true);
|
||||
BackgroundSourceUtils.getInstance(this).saveSettings();
|
||||
BackgroundBean previewBean = utils.getPreviewBackgroundBean();
|
||||
// 修复1:同步裁剪后路径到预览Bean(关键!确保加载路径匹配)
|
||||
// 从压缩文件路径中提取文件名,更新到previewBean的backgroundFileName
|
||||
String cropFileName = fScaledCompressBitmapFile.getName();
|
||||
previewBean.setBackgroundFileName(cropFileName); // 重点:更新为裁剪后的压缩文件名
|
||||
// 修复2:强制设置isUseBackgroundFile=true(确保BackgroundView加载图片,而非透明背景)
|
||||
previewBean.setIsUseBackgroundFile(true);
|
||||
previewBean.setIsUseScaledCompress(true);
|
||||
utils.saveSettings(); // 持久化保存Bean,确保路径同步
|
||||
|
||||
// 修复3:裁剪成功后立即清理临时文件(避免残留冲突)
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "裁剪成功,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
|
||||
// 修复:裁剪成功后刷新预览视图(保持原逻辑,此时路径已同步)
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
|
||||
} else {
|
||||
ToastUtils.show("图片压缩保存失败");
|
||||
BackgroundSourceUtils.getInstance(this).getPreviewBackgroundBean().setIsUseScaledCompress(false);
|
||||
BackgroundSourceUtils.getInstance(this).saveSettings();
|
||||
} else {
|
||||
ToastUtils.show("图片压缩保存失败");
|
||||
BackgroundBean previewBean = utils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseScaledCompress(false);
|
||||
utils.saveSettings();
|
||||
// 修复:保存失败时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "裁剪失败,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
// 修复:保存失败时刷新原始预览图
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
LogUtils.e(TAG, "文件未找到" + e);
|
||||
ToastUtils.show("文件未找到" + e);
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "写入异常" + e);
|
||||
ToastUtils.show("写入异常" + e);
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "流关闭异常" + e);
|
||||
ToastUtils.show("流关闭异常" + e);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
LogUtils.e(TAG, "文件未找到" + e);
|
||||
ToastUtils.show("文件未找到:" + e.getMessage());
|
||||
// 异常时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "写入异常" + e);
|
||||
ToastUtils.show("写入异常:" + e.getMessage());
|
||||
// 异常时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
}
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "流关闭异常" + e);
|
||||
ToastUtils.show("流关闭异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (scaledBitmap != null && !scaledBitmap.isRecycled()) {
|
||||
scaledBitmap.recycle();
|
||||
scaledBitmap.recycle(); // 回收缩放后的Bitmap,避免内存泄漏
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,8 +521,8 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
* 分享图片
|
||||
*/
|
||||
void sharePicture() {
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
File fRecivedPicture = new File(utils.getCurrentBackgroundFilePath());
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
File fRecivedPicture = new File(utils.getCurrentBackgroundFilePath());
|
||||
Uri uri = UriUtil.getUriForFile(this, fRecivedPicture);
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
@@ -519,95 +531,100 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
startActivity(Intent.createChooser(shareIntent, "Share Image"));
|
||||
}
|
||||
|
||||
// public File getRecivedPictureFile() {
|
||||
// BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
// utils.loadSettings();
|
||||
// return new File(utils.getBackgroundSourceDirPath(), _RecivedBackgroundFileName);
|
||||
// }
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_SELECT_PICTURE && resultCode == RESULT_OK) {
|
||||
try {
|
||||
Uri selectedImage = data.getData();
|
||||
LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
|
||||
File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
|
||||
// public void saveToRecivedBackground(String srcFilePath, String srcFillSourcePath) {
|
||||
// BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(this);
|
||||
// utils.loadSettings();
|
||||
// File dstFile = new File(utils.getBackgroundSourceDirPath(), _RecivedBackgroundFileName);
|
||||
// //compressQualityToRecivedPicture(srcFilePath);
|
||||
// ToastUtils.show("compressQualityToRecivedPicture not yet.");
|
||||
// FileUtils.copyFile(new File(srcFilePath), dstFile);
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_SELECT_PICTURE && resultCode == RESULT_OK) {
|
||||
try {
|
||||
Uri selectedImage = data.getData();
|
||||
LogUtils.d(TAG, "Uri is : " + selectedImage.toString());
|
||||
File fSrcImage = new File(UriUtil.getFilePathFromUri(this, selectedImage));
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(this);
|
||||
// 修复:保存图片到预览Bean后,立即刷新BackgroundView显示预览图
|
||||
utils.saveFileToPreviewBean(fSrcImage, selectedImage.toString());
|
||||
startCropImageActivity(false);
|
||||
//mfRecivedPicture = getRecivedPictureFile();
|
||||
// BackgroundBean bean = utils.getPreviewBackgroundBean();
|
||||
// mfRecivedPicture = getRecivedPictureFile();
|
||||
// if (FileUtils.copyFile(fSrcImage, mfRecivedPicture)) {
|
||||
// startCropImageActivity(false);
|
||||
// } else {
|
||||
// ToastUtils.show("图片复制失败,请重试");
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "选择图片异常" + e);
|
||||
ToastUtils.show("选择图片失败:" + e.getMessage());
|
||||
}
|
||||
} else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "REQUEST_TAKE_PHOTO");
|
||||
Bundle extras = data.getExtras();
|
||||
if (extras != null) {
|
||||
Bitmap imageBitmap = (Bitmap) extras.get("data");
|
||||
if (imageBitmap != null) {
|
||||
compressQualityToRecivedPicture(imageBitmap);
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("拍照图片为空");
|
||||
}
|
||||
} else {
|
||||
ToastUtils.show("拍照数据获取失败");
|
||||
}
|
||||
} else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE");
|
||||
try {
|
||||
Bitmap cropBitmap = null;
|
||||
// 方案1:通过Intent获取剪裁后的Bitmap
|
||||
if (data != null && data.hasExtra("data")) {
|
||||
cropBitmap = data.getParcelableExtra("data");
|
||||
} else if (_mSourceCropTempFile.exists()) {
|
||||
LogUtils.d(TAG, String.format("_mSourceCropTempFile Exists, Path is :%s ", _mSourceCropTempFile.getPath()));
|
||||
cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath());
|
||||
} else {
|
||||
ToastUtils.show("剪裁文件不存在");
|
||||
return;
|
||||
}
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
|
||||
if (cropBitmap != null) {
|
||||
saveCropBitmap(cropBitmap);
|
||||
} else {
|
||||
ToastUtils.show("获取剪裁图片失败");
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
LogUtils.e(TAG, "内存溢出" + e);
|
||||
ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "剪裁保存异常" + e);
|
||||
ToastUtils.show("保存失败:" + e.getMessage());
|
||||
}/* finally {
|
||||
// 安全删除临时文件
|
||||
if (mfTempCropPicture.exists()) {
|
||||
mfTempCropPicture.delete();
|
||||
}
|
||||
}*/
|
||||
} else if (resultCode != RESULT_OK) {
|
||||
LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode);
|
||||
ToastUtils.show("操作已取消");
|
||||
}
|
||||
}
|
||||
// 启动裁剪(保持原逻辑,裁剪比例已修复)
|
||||
startCropImageActivity(false);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "选择图片异常" + e);
|
||||
ToastUtils.show("选择图片失败:" + e.getMessage());
|
||||
// 异常时清理临时文件(避免残留)
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "选择图片异常,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
}
|
||||
} else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "REQUEST_TAKE_PHOTO");
|
||||
Bundle extras = data.getExtras();
|
||||
if (extras != null) {
|
||||
Bitmap imageBitmap = (Bitmap) extras.get("data");
|
||||
if (imageBitmap != null) {
|
||||
compressQualityToRecivedPicture(imageBitmap);
|
||||
// 修复:拍照压缩后,刷新预览图
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
startCropImageActivity(false);
|
||||
} else {
|
||||
ToastUtils.show("拍照图片为空");
|
||||
// 图片为空时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "拍照图片为空,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ToastUtils.show("拍照数据获取失败");
|
||||
// 数据获取失败时清理临时文件
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "拍照数据获取失败,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
}
|
||||
} else if (requestCode == REQUEST_CROP_IMAGE && resultCode == RESULT_OK) {
|
||||
LogUtils.d(TAG, "CROP_IMAGE_REQUEST_CODE");
|
||||
try {
|
||||
Bitmap cropBitmap = null;
|
||||
// 方案1:通过Intent获取剪裁后的Bitmap
|
||||
if (data != null && data.hasExtra("data")) {
|
||||
cropBitmap = data.getParcelableExtra("data");
|
||||
} else if (_mSourceCropTempFile.exists()) {
|
||||
LogUtils.d(TAG, String.format("_mSourceCropTempFile Exists, Path is :%s ", _mSourceCropTempFile.getPath()));
|
||||
cropBitmap = BitmapFactory.decodeFile(_mSourceCropTempFile.getPath());
|
||||
} else {
|
||||
ToastUtils.show("剪裁文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cropBitmap != null) {
|
||||
saveCropBitmap(cropBitmap); // 调用保存方法(内含预览刷新+临时文件清理)
|
||||
} else {
|
||||
ToastUtils.show("获取剪裁图片失败");
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
LogUtils.e(TAG, "内存溢出" + e);
|
||||
ToastUtils.show("保存失败:内存不足,请尝试裁剪更小的图片");
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "剪裁保存异常" + e);
|
||||
ToastUtils.show("保存失败:" + e.getMessage());
|
||||
} finally {
|
||||
// 修复核心:裁剪流程结束后,强制清理临时文件(双重保障,避免残留冲突)
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "裁剪流程结束,强制清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
}
|
||||
} else if (resultCode != RESULT_OK) {
|
||||
LogUtils.d(TAG, "操作取消或失败,requestCode: " + requestCode);
|
||||
ToastUtils.show("操作已取消");
|
||||
// 修复:操作取消/失败时,强制清理临时文件(避免影响下次裁剪)
|
||||
if (_mSourceCropTempFile.exists()) {
|
||||
_mSourceCropTempFile.delete();
|
||||
LogUtils.d(TAG, "操作取消/失败,清理临时文件:" + _mSourceCropTempFile.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查类型是否为图片
|
||||
@@ -645,26 +662,25 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
|
||||
void setBackgroundColor() {
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1);
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
void setBackgroundColor() {
|
||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||
int nPixelColor = bean.getPixelColor();
|
||||
RelativeLayout mainLayout = findViewById(R.id.activitybackgroundpictureRelativeLayout1);
|
||||
mainLayout.setBackgroundColor(nPixelColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setBackgroundColor();
|
||||
}
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setBackgroundColor();
|
||||
}
|
||||
|
||||
public void onNetworkBackgroundDialog(View view) {
|
||||
// 在需要显示对话框的地方(如网络状态监听回调中)
|
||||
NetworkBackgroundDialog dialog = new NetworkBackgroundDialog(this, new NetworkBackgroundDialog.OnDialogClickListener() {
|
||||
public void onNetworkBackgroundDialog(View view) {
|
||||
// 在需要显示对话框的地方(如网络状态监听回调中)
|
||||
NetworkBackgroundDialog dialog = new NetworkBackgroundDialog(this, new NetworkBackgroundDialog.OnDialogClickListener() {
|
||||
@Override
|
||||
public void onConfirm(String szConfirmFilePath, String szConfirmFileUrl) {
|
||||
//ToastUtils.show("onConfirm");
|
||||
// 保存预览资源信息
|
||||
preViewFilePath = szConfirmFilePath;
|
||||
preViewFileUrl = szConfirmFileUrl;
|
||||
@@ -673,31 +689,33 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
//ToastUtils.show("onCancel");
|
||||
// 取消逻辑
|
||||
}
|
||||
});
|
||||
|
||||
// 可选:修改对话框标题和内容(适配自定义场景)
|
||||
dialog.setTitle("网络图片下载对话框");
|
||||
dialog.setContent("是否下载地址中的图片资源,作为应用背景图片?");
|
||||
// 可选:修改对话框标题和内容(适配自定义场景)
|
||||
dialog.setTitle("网络图片下载对话框");
|
||||
dialog.setContent("是否下载地址中的图片资源,作为应用背景图片?");
|
||||
|
||||
// 显示对话框
|
||||
dialog.show();
|
||||
// 显示对话框
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface OnRecivedPictureListener {
|
||||
void onRecivedPicture(String srcFilePath, String srcFileUrl);
|
||||
}
|
||||
interface OnRecivedPictureListener {
|
||||
void onRecivedPicture(String srcFilePath, String srcFileUrl);
|
||||
}
|
||||
|
||||
OnRecivedPictureListener onRecivedPictureListener = new OnRecivedPictureListener(){
|
||||
@Override
|
||||
public void onRecivedPicture(String srcFilePath, String srcFileUrl) {
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
utils.saveFileToPreviewBean(new File(srcFilePath), srcFileUrl);
|
||||
startCropImageActivity(true);
|
||||
}
|
||||
};
|
||||
OnRecivedPictureListener onRecivedPictureListener = new OnRecivedPictureListener(){
|
||||
@Override
|
||||
public void onRecivedPicture(String srcFilePath, String srcFileUrl) {
|
||||
BackgroundSourceUtils utils= BackgroundSourceUtils.getInstance(BackgroundSettingsActivity.this);
|
||||
utils.saveFileToPreviewBean(new File(srcFilePath), srcFileUrl);
|
||||
// 修复:网络图片下载后刷新预览
|
||||
bvPreviewBackground.reloadPreviewBackground();
|
||||
startCropImageActivity(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重写finish方法,确保所有退出场景都触发Toast
|
||||
@@ -705,7 +723,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
@Override
|
||||
public void finish() {
|
||||
if (!isCommitSettings) {
|
||||
YesNoAlertDialog.show(this, "应用背景更改提示:", "是否应用预览图片?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
YesNoAlertDialog.show(this, "应用背景更改提示:", "是否应用预览图片?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
|
||||
@Override
|
||||
public void onNo() {
|
||||
@@ -725,9 +743,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
finish();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
super.finish();
|
||||
}
|
||||
} else {
|
||||
super.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,143 +1,172 @@
|
||||
package cc.winboll.studio.powerbell.model;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 11:52:28
|
||||
* @Describe 应用背景图片数据类
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 11:52:28
|
||||
* @Describe 应用背景图片数据类(存储正式/预览背景配置,支持JSON序列化/反序列化)
|
||||
*/
|
||||
public class BackgroundBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "BackgroundPictureBean";
|
||||
|
||||
String backgroundFileName = "";
|
||||
String backgroundFileInfo = "";
|
||||
boolean isUseBackgroundFile = false;
|
||||
String backgroundScaledCompressFileName = "";
|
||||
boolean isUseScaledCompress = false;
|
||||
int backgroundWidth = 100;
|
||||
int backgroundHeight = 100;
|
||||
// 图片拾取像素颜色
|
||||
int pixelColor = 0;
|
||||
// 核心字段:背景图片文件名(对应BackgroundSource目录下的图片文件)
|
||||
private String backgroundFileName = "";
|
||||
// 附加字段:图片信息(如Uri、网络地址等,仅作备注,不参与路径生成)
|
||||
private String backgroundFileInfo = "";
|
||||
// 控制字段:是否启用背景图片(true-显示背景图,false-显示透明背景)
|
||||
private boolean isUseBackgroundFile = false;
|
||||
// 核心字段:压缩后背景图片文件名(对应BackgroundSource目录下的压缩图片)
|
||||
private String backgroundScaledCompressFileName = "";
|
||||
// 控制字段:是否启用压缩背景图(true-加载压缩图,false-加载原图)
|
||||
private boolean isUseScaledCompress = false;
|
||||
// 裁剪比例字段:背景图宽高比(默认1:1,用于固定比例裁剪)
|
||||
private int backgroundWidth = 100;
|
||||
private int backgroundHeight = 100;
|
||||
// 像素拾取字段:拾取的像素颜色(用于纯色背景)
|
||||
private int pixelColor = 0;
|
||||
|
||||
/**
|
||||
* 无参构造器(必须,JSON反序列化时需默认构造器)
|
||||
*/
|
||||
public BackgroundBean() {
|
||||
}
|
||||
|
||||
public void setBackgroundScaledCompressFileName(String backgroundScaledCompressFileName) {
|
||||
this.backgroundScaledCompressFileName = backgroundScaledCompressFileName;
|
||||
}
|
||||
// ====================================== Getter/Setter 方法(全字段,确保序列化/反序列化完整)======================================
|
||||
public String getBackgroundFileName() {
|
||||
return backgroundFileName;
|
||||
}
|
||||
|
||||
public String getBackgroundScaledCompressFileName() {
|
||||
return backgroundScaledCompressFileName;
|
||||
}
|
||||
public void setBackgroundFileName(String backgroundFileName) {
|
||||
this.backgroundFileName = backgroundFileName == null ? "" : backgroundFileName; // 防null,避免空指针
|
||||
}
|
||||
|
||||
public void setIsUseScaledCompress(boolean isUseScaledCompress) {
|
||||
this.isUseScaledCompress = isUseScaledCompress;
|
||||
}
|
||||
public String getBackgroundFileInfo() {
|
||||
return backgroundFileInfo;
|
||||
}
|
||||
|
||||
public boolean isUseScaledCompress() {
|
||||
return isUseScaledCompress;
|
||||
}
|
||||
public void setBackgroundFileInfo(String backgroundFileInfo) {
|
||||
this.backgroundFileInfo = backgroundFileInfo == null ? "" : backgroundFileInfo; // 防null,避免空指针
|
||||
}
|
||||
|
||||
public void setIsUseBackgroundFile(boolean isUseBackgroundFile) {
|
||||
this.isUseBackgroundFile = isUseBackgroundFile;
|
||||
}
|
||||
public boolean isUseBackgroundFile() {
|
||||
return isUseBackgroundFile;
|
||||
}
|
||||
|
||||
public boolean isUseBackgroundFile() {
|
||||
return isUseBackgroundFile;
|
||||
}
|
||||
public void setIsUseBackgroundFile(boolean isUseBackgroundFile) {
|
||||
this.isUseBackgroundFile = isUseBackgroundFile;
|
||||
}
|
||||
|
||||
public void setBackgroundFileInfo(String backgroundFileInfo) {
|
||||
this.backgroundFileInfo = backgroundFileInfo;
|
||||
}
|
||||
public String getBackgroundScaledCompressFileName() {
|
||||
return backgroundScaledCompressFileName;
|
||||
}
|
||||
|
||||
public String getBackgroundFileInfo() {
|
||||
return backgroundFileInfo;
|
||||
}
|
||||
public void setBackgroundScaledCompressFileName(String backgroundScaledCompressFileName) {
|
||||
this.backgroundScaledCompressFileName = backgroundScaledCompressFileName == null ? "" : backgroundScaledCompressFileName; // 防null
|
||||
}
|
||||
|
||||
public void setBackgroundFileName(String backgroundFileName) {
|
||||
this.backgroundFileName = backgroundFileName;
|
||||
}
|
||||
public boolean isUseScaledCompress() {
|
||||
return isUseScaledCompress;
|
||||
}
|
||||
|
||||
public String getBackgroundFileName() {
|
||||
return backgroundFileName;
|
||||
}
|
||||
|
||||
public void setPixelColor(int pixelColor) {
|
||||
this.pixelColor = pixelColor;
|
||||
}
|
||||
|
||||
public int getPixelColor() {
|
||||
return pixelColor;
|
||||
}
|
||||
|
||||
public void setBackgroundWidth(int backgroundWidth) {
|
||||
this.backgroundWidth = backgroundWidth;
|
||||
public void setIsUseScaledCompress(boolean isUseScaledCompress) {
|
||||
this.isUseScaledCompress = isUseScaledCompress;
|
||||
}
|
||||
|
||||
public int getBackgroundWidth() {
|
||||
return backgroundWidth;
|
||||
}
|
||||
|
||||
public void setBackgroundHeight(int backgroundHeight) {
|
||||
this.backgroundHeight = backgroundHeight;
|
||||
public void setBackgroundWidth(int backgroundWidth) {
|
||||
this.backgroundWidth = backgroundWidth <= 0 ? 100 : backgroundWidth; // 防无效值,确保宽高比有效
|
||||
}
|
||||
|
||||
public int getBackgroundHeight() {
|
||||
return backgroundHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BackgroundBean.class.getName();
|
||||
public void setBackgroundHeight(int backgroundHeight) {
|
||||
this.backgroundHeight = backgroundHeight <= 0 ? 100 : backgroundHeight; // 防无效值,确保宽高比有效
|
||||
}
|
||||
|
||||
public int getPixelColor() {
|
||||
return pixelColor;
|
||||
}
|
||||
|
||||
public void setPixelColor(int pixelColor) {
|
||||
this.pixelColor = pixelColor;
|
||||
}
|
||||
|
||||
// ====================================== 序列化/反序列化方法(适配JSON读写,确保数据持久化完整)======================================
|
||||
@Override
|
||||
public String getName() {
|
||||
return BackgroundBean.class.getName(); // 必须重写,BaseBean序列化时需类名标识
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Bean数据写入JSON(序列化,保存到文件)
|
||||
* 确保所有字段都被写入,无遗漏
|
||||
*/
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
BackgroundBean bean = this;
|
||||
jsonWriter.name("backgroundFileName").value(bean.getBackgroundFileName());
|
||||
jsonWriter.name("backgroundFileInfo").value(bean.getBackgroundFileInfo());
|
||||
jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile());
|
||||
jsonWriter.name("backgroundScaledCompressFileName").value(bean.getBackgroundScaledCompressFileName());
|
||||
jsonWriter.name("isUseScaledCompress").value(bean.isUseScaledCompress());
|
||||
jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth());
|
||||
jsonWriter.name("backgroundFileInfo").value(bean.getBackgroundFileInfo());
|
||||
jsonWriter.name("isUseBackgroundFile").value(bean.isUseBackgroundFile());
|
||||
jsonWriter.name("backgroundScaledCompressFileName").value(bean.getBackgroundScaledCompressFileName());
|
||||
jsonWriter.name("isUseScaledCompress").value(bean.isUseScaledCompress());
|
||||
jsonWriter.name("backgroundWidth").value(bean.getBackgroundWidth());
|
||||
jsonWriter.name("backgroundHeight").value(bean.getBackgroundHeight());
|
||||
jsonWriter.name("pixelColor").value(bean.getPixelColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JSON读取数据到Bean(反序列化,从文件加载)
|
||||
* 确保所有字段都被读取,兼容旧版本(无字段时设默认值)
|
||||
*/
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
BackgroundBean bean = new BackgroundBean();
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("backgroundFileName")) {
|
||||
bean.setBackgroundFileName(jsonReader.nextString());
|
||||
} else if (name.equals("backgroundFileInfo")) {
|
||||
bean.setBackgroundFileInfo(jsonReader.nextString());
|
||||
} else if (name.equals("isUseBackgroundFile")) {
|
||||
bean.setIsUseBackgroundFile(jsonReader.nextBoolean());
|
||||
} else if (name.equals("backgroundScaledCompressFileName")) {
|
||||
bean.setBackgroundScaledCompressFileName(jsonReader.nextString());
|
||||
} else if (name.equals("isUseScaledCompress")) {
|
||||
bean.setIsUseScaledCompress(jsonReader.nextBoolean());
|
||||
} else if (name.equals("backgroundWidth")) {
|
||||
bean.setBackgroundWidth(jsonReader.nextInt());
|
||||
} else if (name.equals("backgroundHeight")) {
|
||||
bean.setBackgroundHeight(jsonReader.nextInt());
|
||||
} else if (name.equals("pixelColor")) {
|
||||
bean.setPixelColor(jsonReader.nextInt());
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
switch (name) {
|
||||
case "backgroundFileName":
|
||||
bean.setBackgroundFileName(jsonReader.nextString());
|
||||
break;
|
||||
case "backgroundFileInfo":
|
||||
bean.setBackgroundFileInfo(jsonReader.nextString());
|
||||
break;
|
||||
case "isUseBackgroundFile":
|
||||
bean.setIsUseBackgroundFile(jsonReader.nextBoolean());
|
||||
break;
|
||||
case "backgroundScaledCompressFileName":
|
||||
bean.setBackgroundScaledCompressFileName(jsonReader.nextString());
|
||||
break;
|
||||
case "isUseScaledCompress":
|
||||
bean.setIsUseScaledCompress(jsonReader.nextBoolean());
|
||||
break;
|
||||
case "backgroundWidth":
|
||||
bean.setBackgroundWidth(jsonReader.nextInt());
|
||||
break;
|
||||
case "backgroundHeight":
|
||||
bean.setBackgroundHeight(jsonReader.nextInt());
|
||||
break;
|
||||
case "pixelColor":
|
||||
bean.setPixelColor(jsonReader.nextInt());
|
||||
break;
|
||||
default:
|
||||
jsonReader.skipValue(); // 跳过未知字段,兼容旧版本Bean
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,52 +3,48 @@ package cc.winboll.studio.powerbell.utils;
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 12:07:20
|
||||
* @Describe 背景图片工具集(修复单例模式,线程安全)
|
||||
* @Describe 背景图片工具集(修复单例模式,线程安全+数据流转正常)
|
||||
*/
|
||||
public class BackgroundSourceUtils {
|
||||
|
||||
public static final String TAG = "BackgroundPictureUtils";
|
||||
|
||||
// 1. 静态实例加volatile,禁止指令重排,保证可见性
|
||||
// 1. 静态实例加volatile,禁止指令重排,保证可见性(双重校验锁单例核心)
|
||||
private static volatile BackgroundSourceUtils sInstance;
|
||||
private Context mContext;
|
||||
private File currentBackgroundBeanFile;
|
||||
private File currentBackgroundBeanFile;
|
||||
private BackgroundBean currentBackgroundBean;
|
||||
private File previewBackgroundBeanFile;
|
||||
private File previewBackgroundBeanFile;
|
||||
private BackgroundBean previewBackgroundBean;
|
||||
// 应用外部存储文件夹路径
|
||||
private File fUtilsDir;
|
||||
private File fModelDir;
|
||||
// 背景图片目录
|
||||
// 应用外部存储文件夹路径(按功能划分目录,避免路径混乱)
|
||||
private File fUtilsDir;
|
||||
private File fModelDir;
|
||||
// 背景图片源文件目录(存储正式/预览图片)
|
||||
private File fBackgroundSourceDir;
|
||||
|
||||
// 2. 私有构造器(加防反射逻辑)
|
||||
// 2. 私有构造器(加防反射逻辑+初始化目录)
|
||||
private BackgroundSourceUtils(Context context) {
|
||||
// 防反射破坏:若已有实例,抛异常阻止创建
|
||||
// 防反射破坏:若已有实例,抛异常阻止重复创建
|
||||
if (sInstance != null) {
|
||||
throw new RuntimeException("BackgroundSourceUtils 是单例类,禁止重复创建!");
|
||||
}
|
||||
// 上下文建议用Application Context,避免内存泄漏
|
||||
// 上下文用Application Context,避免Activity内存泄漏
|
||||
this.mContext = context.getApplicationContext();
|
||||
fUtilsDir = this.mContext.getExternalFilesDir(TAG);
|
||||
fModelDir = new File(fUtilsDir, "ModelDir");
|
||||
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
|
||||
previewBackgroundBeanFile = new File(fModelDir, "previewBackgroundBean.json");
|
||||
fBackgroundSourceDir = new File(fUtilsDir, "BackgroundSource");
|
||||
|
||||
// 加载配置
|
||||
// 初始化目录(按功能划分,确保目录存在)
|
||||
initDirs();
|
||||
// 加载配置(正式/预览Bean)
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
// 3. 双重校验锁单例(线程安全,高效)
|
||||
// 3. 双重校验锁单例(线程安全,高效,支持多线程并发调用)
|
||||
public static BackgroundSourceUtils getInstance(Context context) {
|
||||
// 第一重校验:避免每次调用都加锁(提高效率)
|
||||
// 第一重校验:避免每次调用都加锁(提升效率)
|
||||
if (sInstance == null) {
|
||||
// 同步锁:保证同一时刻只有一个线程进入创建逻辑
|
||||
synchronized (BackgroundSourceUtils.class) {
|
||||
@@ -61,84 +57,202 @@ public class BackgroundSourceUtils {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/*
|
||||
* 加载背景图片配置数据
|
||||
*/
|
||||
/**
|
||||
* 初始化所有目录(确保目录存在,避免文件操作失败)
|
||||
*/
|
||||
private void initDirs() {
|
||||
// 工具类根目录(外部存储:/Android/data/包名/files/BackgroundPictureUtils)
|
||||
fUtilsDir = mContext.getExternalFilesDir(TAG);
|
||||
if (fUtilsDir == null) {
|
||||
LogUtils.e(TAG, "外部存储不可用,无法初始化目录");
|
||||
return;
|
||||
}
|
||||
// 模型文件目录(存储BackgroundBean的JSON文件)
|
||||
fModelDir = new File(fUtilsDir, "ModelDir");
|
||||
// 背景图片源目录(存储正式/预览图片文件)
|
||||
fBackgroundSourceDir = new File(fUtilsDir, "BackgroundSource");
|
||||
|
||||
// 递归创建所有目录(确保目录存在)
|
||||
if (!fModelDir.exists()) {
|
||||
fModelDir.mkdirs();
|
||||
LogUtils.d(TAG, "创建模型文件目录:" + fModelDir.getAbsolutePath());
|
||||
}
|
||||
if (!fBackgroundSourceDir.exists()) {
|
||||
fBackgroundSourceDir.mkdirs();
|
||||
LogUtils.d(TAG, "创建背景图片目录:" + fBackgroundSourceDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 初始化Bean文件对象(存储JSON配置)
|
||||
currentBackgroundBeanFile = new File(fModelDir, "currentBackgroundBean.json");
|
||||
previewBackgroundBeanFile = new File(fModelDir, "previewBackgroundBean.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载背景图片配置数据(正式/预览Bean)
|
||||
* 从JSON文件读取,若文件不存在则创建默认Bean并保存
|
||||
*/
|
||||
void loadSettings() {
|
||||
// 加载正式Bean
|
||||
currentBackgroundBean = BackgroundBean.loadBeanFromFile(currentBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
||||
if (currentBackgroundBean == null) {
|
||||
currentBackgroundBean = new BackgroundBean();
|
||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
||||
LogUtils.d(TAG, "正式背景Bean不存在,创建默认Bean");
|
||||
}
|
||||
previewBackgroundBean = BackgroundBean.loadBeanFromFile(previewBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
||||
|
||||
// 加载预览Bean
|
||||
previewBackgroundBean = BackgroundBean.loadBeanFromFile(previewBackgroundBeanFile.getAbsolutePath(), BackgroundBean.class);
|
||||
if (previewBackgroundBean == null) {
|
||||
previewBackgroundBean = new BackgroundBean();
|
||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
||||
LogUtils.d(TAG, "预览背景Bean不存在,创建默认Bean");
|
||||
}
|
||||
}
|
||||
|
||||
public BackgroundBean getCurrentBackgroundBean() {
|
||||
return currentBackgroundBean;
|
||||
}
|
||||
|
||||
public BackgroundBean getPreviewBackgroundBean() {
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
public String getCurrentBackgroundFilePath() {
|
||||
loadSettings();
|
||||
File file = new File(fBackgroundSourceDir, currentBackgroundBean.getBackgroundFileName());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getPreviewBackgroundFilePath() {
|
||||
loadSettings();
|
||||
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundFileName());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public String getPreviewBackgroundScaledCompressFilePath() {
|
||||
loadSettings();
|
||||
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundScaledCompressFileName());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
||||
/**
|
||||
* 获取正式背景Bean(对外提供,用于修改正式配置)
|
||||
*/
|
||||
public BackgroundBean getCurrentBackgroundBean() {
|
||||
return currentBackgroundBean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取预览背景Bean(对外提供,用于修改预览配置)
|
||||
*/
|
||||
public BackgroundBean getPreviewBackgroundBean() {
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正式背景图片路径(拼接:背景目录+正式Bean中的文件名)
|
||||
*/
|
||||
public String getCurrentBackgroundFilePath() {
|
||||
loadSettings(); // 加载最新配置,避免数据滞后
|
||||
File file = new File(fBackgroundSourceDir, currentBackgroundBean.getBackgroundFileName());
|
||||
LogUtils.d(TAG, "正式背景路径:" + file.getAbsolutePath());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预览背景图片路径(拼接:背景目录+预览Bean中的文件名)
|
||||
*/
|
||||
public String getPreviewBackgroundFilePath() {
|
||||
loadSettings(); // 加载最新配置,避免数据滞后
|
||||
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundFileName());
|
||||
LogUtils.d(TAG, "预览背景路径:" + file.getAbsolutePath());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预览背景压缩图片路径(拼接:背景目录+预览Bean中的压缩文件名)
|
||||
*/
|
||||
public String getPreviewBackgroundScaledCompressFilePath() {
|
||||
loadSettings(); // 加载最新配置,避免数据滞后
|
||||
File file = new File(fBackgroundSourceDir, previewBackgroundBean.getBackgroundScaledCompressFileName());
|
||||
LogUtils.d(TAG, "预览压缩背景路径:" + file.getAbsolutePath());
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置(将正式/预览Bean同步到JSON文件,持久化存储)
|
||||
*/
|
||||
public void saveSettings() {
|
||||
BackgroundBean.saveBeanToFile(currentBackgroundBeanFile.getAbsolutePath(), currentBackgroundBean);
|
||||
BackgroundBean.saveBeanToFile(previewBackgroundBeanFile.getAbsolutePath(), previewBackgroundBean);
|
||||
LogUtils.d(TAG, "配置保存成功:正式Bean=" + currentBackgroundBeanFile.getAbsolutePath() + ",预览Bean=" + previewBackgroundBeanFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取背景图片源目录路径(对外提供,用于创建临时文件)
|
||||
*/
|
||||
public String getBackgroundSourceDirPath() {
|
||||
return fBackgroundSourceDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
/*
|
||||
* 保存图片到预览模型, 并返回预览模型数据
|
||||
*/
|
||||
public BackgroundBean saveFileToPreviewBean(File sourceFile, String fileInfo) {
|
||||
File previewBackgroundFile = new File(fBackgroundSourceDir, FileUtils.createUniqueFileName(sourceFile));
|
||||
//ToastUtils.show(String.format("saveFileToPreviewBean previewBackgroundFile : %s", previewBackgroundFile.getAbsolutePath()));
|
||||
|
||||
FileUtils.copyFile(sourceFile, previewBackgroundFile);
|
||||
previewBackgroundBean = new BackgroundBean();
|
||||
previewBackgroundBean.setBackgroundFileName(previewBackgroundFile.getName());
|
||||
previewBackgroundBean.setBackgroundScaledCompressFileName("ScaledCompress_"+previewBackgroundFile.getName());
|
||||
previewBackgroundBean.setBackgroundFileName(fileInfo);
|
||||
saveSettings();
|
||||
|
||||
ToastUtils.show(String.format("saveFileToPreviewBean getPreviewBackgroundFilePath() : %s", getPreviewBackgroundFilePath()));
|
||||
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
public void commitPreviewSourceToCurrent() {
|
||||
currentBackgroundBean = previewBackgroundBean;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
public void setCurrentSourceToPreview() {
|
||||
previewBackgroundBean = currentBackgroundBean;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存图片到预览Bean(核心修复:解决文件名覆盖+路径无效问题)
|
||||
* @param sourceFile 源图片文件(非空,必须存在)
|
||||
* @param fileInfo 图片附加信息(如Uri字符串,仅作备注)
|
||||
* @return 更新后的预览Bean
|
||||
*/
|
||||
public BackgroundBean saveFileToPreviewBean(File sourceFile, String fileInfo) {
|
||||
// 校验源文件合法性
|
||||
if (sourceFile == null || !sourceFile.exists() || !sourceFile.isFile()) {
|
||||
LogUtils.e(TAG, "源文件无效:" + (sourceFile != null ? sourceFile.getAbsolutePath() : "null"));
|
||||
ToastUtils.show("源图片文件无效");
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
// 确保背景目录存在(防止首次使用时目录未创建)
|
||||
if (!fBackgroundSourceDir.exists()) {
|
||||
fBackgroundSourceDir.mkdirs();
|
||||
LogUtils.d(TAG, "背景目录不存在,自动创建:" + fBackgroundSourceDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 生成唯一文件名(基于源文件后缀,避免重复)
|
||||
String uniqueFileName = FileUtils.createUniqueFileName(sourceFile);
|
||||
File previewBackgroundFile = new File(fBackgroundSourceDir, uniqueFileName);
|
||||
|
||||
// 复制源文件到预览目录(确保图片实际保存成功)
|
||||
boolean copySuccess = FileUtils.copyFile(sourceFile, previewBackgroundFile);
|
||||
if (!copySuccess) {
|
||||
LogUtils.e(TAG, "图片复制到预览目录失败:" + sourceFile.getAbsolutePath() + " → " + previewBackgroundFile.getAbsolutePath());
|
||||
ToastUtils.show("预览图片保存失败");
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
// 正确赋值预览Bean(核心修复:不覆盖文件名,将附加信息存入backgroundFileInfo)
|
||||
previewBackgroundBean = new BackgroundBean();
|
||||
previewBackgroundBean.setBackgroundFileName(previewBackgroundFile.getName()); // 正确赋值:唯一文件名
|
||||
previewBackgroundBean.setBackgroundScaledCompressFileName("ScaledCompress_" + previewBackgroundFile.getName()); // 压缩文件名(前缀标识)
|
||||
previewBackgroundBean.setBackgroundFileInfo(fileInfo); // 正确赋值:附加信息(Uri)
|
||||
previewBackgroundBean.setIsUseBackgroundFile(true); // 标记使用背景图,确保BackgroundView加载
|
||||
previewBackgroundBean.setBackgroundWidth(100); // 默认宽高比1:1(可根据需求调整)
|
||||
previewBackgroundBean.setBackgroundHeight(100);
|
||||
saveSettings(); // 持久化保存预览Bean
|
||||
|
||||
LogUtils.d(TAG, "预览图片保存成功:" + previewBackgroundFile.getAbsolutePath());
|
||||
ToastUtils.show("预览图片加载成功");
|
||||
return previewBackgroundBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交预览背景到正式背景(将预览Bean深拷贝到正式Bean,应用预览配置)
|
||||
*/
|
||||
public void commitPreviewSourceToCurrent() {
|
||||
// 深拷贝:新建正式Bean,复制预览Bean的所有字段(避免浅拷贝导致数据污染)
|
||||
currentBackgroundBean = new BackgroundBean();
|
||||
currentBackgroundBean.setBackgroundFileName(previewBackgroundBean.getBackgroundFileName());
|
||||
currentBackgroundBean.setBackgroundFileInfo(previewBackgroundBean.getBackgroundFileInfo());
|
||||
currentBackgroundBean.setIsUseBackgroundFile(previewBackgroundBean.isUseBackgroundFile());
|
||||
currentBackgroundBean.setBackgroundScaledCompressFileName(previewBackgroundBean.getBackgroundScaledCompressFileName());
|
||||
currentBackgroundBean.setIsUseScaledCompress(previewBackgroundBean.isUseScaledCompress());
|
||||
currentBackgroundBean.setBackgroundWidth(previewBackgroundBean.getBackgroundWidth());
|
||||
currentBackgroundBean.setBackgroundHeight(previewBackgroundBean.getBackgroundHeight());
|
||||
currentBackgroundBean.setPixelColor(previewBackgroundBean.getPixelColor());
|
||||
|
||||
saveSettings(); // 持久化保存正式Bean
|
||||
LogUtils.d(TAG, "预览背景提交成功:正式背景更新为预览背景");
|
||||
ToastUtils.show("背景图片应用成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将正式背景同步到预览背景(深拷贝,初始化预览为当前正式背景)
|
||||
*/
|
||||
public void setCurrentSourceToPreview() {
|
||||
// 深拷贝:新建预览Bean,复制正式Bean的所有字段(避免浅拷贝导致数据污染)
|
||||
previewBackgroundBean = new BackgroundBean();
|
||||
previewBackgroundBean.setBackgroundFileName(currentBackgroundBean.getBackgroundFileName());
|
||||
previewBackgroundBean.setBackgroundFileInfo(currentBackgroundBean.getBackgroundFileInfo());
|
||||
previewBackgroundBean.setIsUseBackgroundFile(currentBackgroundBean.isUseBackgroundFile());
|
||||
previewBackgroundBean.setBackgroundScaledCompressFileName(currentBackgroundBean.getBackgroundScaledCompressFileName());
|
||||
previewBackgroundBean.setIsUseScaledCompress(currentBackgroundBean.isUseScaledCompress());
|
||||
previewBackgroundBean.setBackgroundWidth(currentBackgroundBean.getBackgroundWidth());
|
||||
previewBackgroundBean.setBackgroundHeight(currentBackgroundBean.getBackgroundHeight());
|
||||
previewBackgroundBean.setPixelColor(currentBackgroundBean.getPixelColor());
|
||||
|
||||
saveSettings(); // 持久化保存预览Bean
|
||||
LogUtils.d(TAG, "正式背景同步到预览:预览背景更新为当前正式背景");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,33 +10,28 @@ import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.powerbell.R;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/19 18:01
|
||||
* @Describe 背景图片视图控件(全透明背景 + 不拉伸居中平铺 + 完全填充父视图)
|
||||
* @Describe 背景图片视图控件(全透明背景 + 不拉伸居中平铺 + 完全填充父视图 + 预览/正式模式切换)
|
||||
*/
|
||||
public class BackgroundView extends RelativeLayout {
|
||||
|
||||
public static final String TAG = "BackgroundView";
|
||||
|
||||
Context mContext;
|
||||
private Context mContext;
|
||||
private ImageView ivBackground;
|
||||
private BackgroundSourceUtils backgroundSourceUtils; // 工具类实例(避免重复创建)
|
||||
|
||||
private static String BACKGROUND_IMAGE_FOLDER = "Background";
|
||||
private static String BACKGROUND_IMAGE_FILENAME = "current.data";
|
||||
private static String BACKGROUND_IMAGE_PREVIEW_FILENAME = "current_preview.data";
|
||||
private static String backgroundSourceFilePath;
|
||||
private float imageAspectRatio = 1.0f; // 图片原始宽高比(控制不拉伸)
|
||||
// 标记当前是否处于预览模式
|
||||
// 标记当前是否处于预览模式(用于区分加载预览/正式背景)
|
||||
private boolean isPreviewMode = false;
|
||||
|
||||
// 构造器(兼容所有布局场景)
|
||||
public BackgroundView(Context context) {
|
||||
super(context);
|
||||
this.mContext = context;
|
||||
@@ -61,223 +56,134 @@ public class BackgroundView extends RelativeLayout {
|
||||
initView();
|
||||
}
|
||||
|
||||
void initView() {
|
||||
// 1. 控件本身:完全填充父视图 + 全透明背景 + 无内边距
|
||||
/**
|
||||
* 初始化视图(控件本身+内部ImageView)
|
||||
*/
|
||||
private void initView() {
|
||||
// 1. 控件本身配置:完全填充父视图 + 全透明背景 + 无内边距
|
||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
setPadding(0, 0, 0, 0); // 取消自身内边距
|
||||
setBackgroundColor(0x00000000); // 全透明背景(#00000000)
|
||||
setBackground(new ColorDrawable(0x00000000)); // 双重保障:同时设置Background为透明(兼容低版本)
|
||||
setPadding(0, 0, 0, 0); // 取消自身内边距(避免父容器与控件间缝隙)
|
||||
setBackgroundColor(0x00000000); // 全透明背景(ARGB:透明+黑色,无视觉影响)
|
||||
setBackground(new ColorDrawable(0x00000000)); // 双重保障:兼容Android低版本,确保背景透明
|
||||
|
||||
initBackgroundImageView(); // 初始化内部ImageView(全透明 + 不拉伸居中平铺)
|
||||
// 初始化工具类(单例,全局唯一)
|
||||
backgroundSourceUtils = BackgroundSourceUtils.getInstance(mContext);
|
||||
|
||||
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
|
||||
loadAndSetImageViewBackground();
|
||||
// 2. 初始化内部ImageView(核心:不拉伸+居中平铺+全透明)
|
||||
initBackgroundImageView();
|
||||
|
||||
// 3. 初始加载:默认加载正式背景
|
||||
reloadCurrentBackground();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化内部ImageView(配置尺寸、缩放模式、背景等)
|
||||
*/
|
||||
private void initBackgroundImageView() {
|
||||
ivBackground = new ImageView(mContext);
|
||||
// 2. ImageView:初始宽高WRAP_CONTENT + 居中 + 无内边距 + 全透明背景
|
||||
// 基础布局:宽高WRAP_CONTENT(跟随图片比例)+ 居中显示 + 无内边距/边距
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 居中显示
|
||||
layoutParams.setMargins(0, 0, 0, 0); // 取消边距
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 核心:ImageView在控件中居中
|
||||
layoutParams.setMargins(0, 0, 0, 0); // 取消边距(避免ImageView与控件间缝隙)
|
||||
ivBackground.setLayoutParams(layoutParams);
|
||||
|
||||
// 3. 缩放模式:FIT_CENTER(不拉伸,按比例显示)
|
||||
// 关键配置:缩放模式FIT_CENTER(不拉伸,按原始比例显示)
|
||||
ivBackground.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
ivBackground.setPadding(0, 0, 0, 0); // 取消内部padding
|
||||
ivBackground.setPadding(0, 0, 0, 0); // 取消内部padding(避免图片与ImageView间缝隙)
|
||||
ivBackground.setBackgroundColor(0x00000000); // ImageView背景全透明
|
||||
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 双重保障(兼容低版本)
|
||||
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 低版本兼容
|
||||
|
||||
this.addView(ivBackground);
|
||||
this.addView(ivBackground); // 添加到父容器(控件本身)
|
||||
}
|
||||
|
||||
// private void initBackgroundImagePath() {
|
||||
// if (isPreviewMode) {
|
||||
// backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getPreviewBackgroundFilePath();
|
||||
// } else {
|
||||
// backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 拷贝图片文件到背景资源目录(正式背景)
|
||||
*/
|
||||
// public void setImageViewSource(String srcBackgroundPath) {
|
||||
// initBackgroundImagePath();
|
||||
// if (backgroundSourceFilePath == null) {
|
||||
// LogUtils.e(TAG, "目标路径初始化失败,无法保存背景图片");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// File srcFile = new File(srcBackgroundPath);
|
||||
// if (!srcFile.exists() || !srcFile.isFile()) {
|
||||
// LogUtils.e(TAG, String.format("源文件不存在或不是文件:%s", srcBackgroundPath));
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// File destFile = new File(backgroundSourceFilePath);
|
||||
// File destDir = destFile.getParentFile();
|
||||
// if (destDir != null && !destDir.exists()) {
|
||||
// boolean isDirCreated = destDir.mkdirs();
|
||||
// if (!isDirCreated) {
|
||||
// LogUtils.e(TAG, "目标目录创建失败:" + destDir.getAbsolutePath());
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// FileInputStream fis = null;
|
||||
// FileOutputStream fos = null;
|
||||
// try {
|
||||
// fis = new FileInputStream(srcFile);
|
||||
// fos = new FileOutputStream(destFile);
|
||||
//
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int len;
|
||||
// while ((len = fis.read(buffer)) != -1) {
|
||||
// fos.write(buffer, 0, len);
|
||||
// }
|
||||
// fos.flush();
|
||||
//
|
||||
// LogUtils.d(TAG, String.format("文件拷贝成功:%s -> %s", srcBackgroundPath, backgroundSourceFilePath));
|
||||
// // 拷贝成功后,若处于预览模式则退出预览,加载正式背景
|
||||
// if (isPreviewMode) {
|
||||
// exitPreviewMode();
|
||||
// } else {
|
||||
// loadAndSetImageViewBackground();
|
||||
// }
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, String.format("文件拷贝失败:%s", e.getMessage()), e);
|
||||
// if (destFile.exists()) {
|
||||
// destFile.delete();
|
||||
// LogUtils.d(TAG, "已删除损坏的目标文件");
|
||||
// }
|
||||
// } finally {
|
||||
// if (fis != null) {
|
||||
// try {
|
||||
// fis.close();
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "输入流关闭失败:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// if (fos != null) {
|
||||
// try {
|
||||
// fos.close();
|
||||
// } catch (Exception e) {
|
||||
// LogUtils.e(TAG, "输出流关闭失败:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 预览临时图片(全透明背景 + 不拉伸居中平铺)
|
||||
* @param previewImagePath 临时预览图片的路径
|
||||
*/
|
||||
/*public void previewBackgroundImage(String previewImagePath) {
|
||||
if (previewImagePath == null || previewImagePath.isEmpty()) {
|
||||
LogUtils.e(TAG, "预览图片路径为空");
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
File previewFile = new File(previewImagePath);
|
||||
if (!previewFile.exists() || !previewFile.isFile()) {
|
||||
LogUtils.e(TAG, "预览图片不存在或不是文件:" + previewImagePath);
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算图片原始宽高比
|
||||
if (!calculateImageAspectRatio(previewFile)) {
|
||||
LogUtils.e(TAG, "预览图片尺寸无效,无法预览");
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩加载预览图片(保持比例)
|
||||
Bitmap previewBitmap = decodeBitmapWithCompress(previewFile, 1080, 1920);
|
||||
if (previewBitmap == null) {
|
||||
LogUtils.e(TAG, "预览图片加载失败");
|
||||
setDefaultImageViewBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置预览图片(保持ImageView透明背景)
|
||||
Drawable previewDrawable = new BitmapDrawable(mContext.getResources(), previewBitmap);
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
ivBackground.setBackground(previewDrawable);
|
||||
} else {
|
||||
ivBackground.setBackgroundDrawable(previewDrawable);
|
||||
}
|
||||
|
||||
// 调整ImageView尺寸(居中平铺,不拉伸)
|
||||
adjustImageViewSize();
|
||||
isPreviewMode = true;
|
||||
LogUtils.d(TAG, "进入预览模式,预览图片路径:" + previewImagePath + ",宽高比:" + imageAspectRatio);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 退出预览模式,恢复显示正式背景图片
|
||||
*/
|
||||
/*public void exitPreviewMode() {
|
||||
if (isPreviewMode) {
|
||||
loadAndSetImageViewBackground();
|
||||
isPreviewMode = false;
|
||||
LogUtils.d(TAG, "退出预览模式,恢复正式背景");
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 公共函数:供外部类调用,重新加载正式背景图片(刷新显示)
|
||||
* 【对外提供】重新加载正式背景图片(从正式Bean获取路径)
|
||||
* 用于:退出预览模式、恢复默认背景、正式背景更新后刷新
|
||||
*/
|
||||
public void reloadCurrentBackground() {
|
||||
LogUtils.d(TAG, "外部调用重新加载背景图片");
|
||||
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getCurrentBackgroundFilePath();
|
||||
loadAndSetImageViewBackground();
|
||||
LogUtils.d(TAG, "=== 开始重新加载正式背景 ===");
|
||||
isPreviewMode = false; // 标记为正式模式
|
||||
// 从工具类获取最新正式背景路径(确保路径同步)
|
||||
String backgroundPath = backgroundSourceUtils.getCurrentBackgroundFilePath();
|
||||
// 加载并显示图片
|
||||
loadAndSetImageViewBackground(backgroundPath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 【对外提供】重新加载预览背景图片(从预览Bean获取路径)
|
||||
* 修复:增加isUseBackgroundFile校验,确保启用背景图时才加载
|
||||
*/
|
||||
public void reloadPreviewBackground() {
|
||||
LogUtils.d(TAG, "外部调用重新加载背景图片");
|
||||
backgroundSourceFilePath = BackgroundSourceUtils.getInstance(this.mContext).getPreviewBackgroundFilePath();
|
||||
loadAndSetImageViewBackground();
|
||||
LogUtils.d(TAG, "=== 开始重新加载预览背景 ===");
|
||||
isPreviewMode = true; // 标记为预览模式
|
||||
// 从工具类获取最新预览背景Bean
|
||||
BackgroundBean previewBean = backgroundSourceUtils.getPreviewBackgroundBean();
|
||||
// 修复:若未启用背景图,直接显示透明背景(避免无效加载)
|
||||
if (!previewBean.isUseBackgroundFile()) {
|
||||
LogUtils.d(TAG, "预览Bean未启用背景图(isUseBackgroundFile=false),显示透明背景");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
// 优先加载压缩图,无则加载原图(保持原逻辑)
|
||||
String backgroundPath;
|
||||
if (previewBean.isUseScaledCompress()) {
|
||||
backgroundPath = backgroundSourceUtils.getPreviewBackgroundScaledCompressFilePath();
|
||||
} else {
|
||||
backgroundPath = backgroundSourceUtils.getPreviewBackgroundFilePath();
|
||||
}
|
||||
// 加载并显示图片
|
||||
loadAndSetImageViewBackground(backgroundPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【对外提供】预览指定路径的临时图片(直接传入路径,不依赖Bean)
|
||||
* 用于:临时预览本地图片、测试图片等场景
|
||||
* @param previewImagePath 临时图片绝对路径(空则显示透明)
|
||||
*/
|
||||
public void previewBackgroundImage(String previewImagePath) {
|
||||
LogUtils.d(TAG, "=== 开始预览指定路径图片 ===");
|
||||
isPreviewMode = true; // 标记为预览模式
|
||||
// 加载并显示指定路径图片
|
||||
loadAndSetImageViewBackground(previewImagePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载正式背景图片并设置到 ImageView(全透明背景 + 不拉伸居中平铺)
|
||||
* 【核心逻辑】加载图片并设置到ImageView(统一处理,避免重复代码)
|
||||
* @param imagePath 图片绝对路径(可为空)
|
||||
*/
|
||||
private void loadAndSetImageViewBackground() {
|
||||
if (backgroundSourceFilePath == null) {
|
||||
LogUtils.d(TAG, "backgroundSourceFilePath == null");
|
||||
setDefaultImageViewBackground();
|
||||
private void loadAndSetImageViewBackground(String imagePath) {
|
||||
// 1. 路径校验(空路径/无效路径 → 显示透明背景)
|
||||
if (imagePath == null || imagePath.isEmpty()) {
|
||||
LogUtils.e(TAG, "图片路径为空,显示透明背景");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
//ToastUtils.show(String.format("backgroundSourceFilePath : %s", backgroundSourceFilePath));
|
||||
|
||||
File backgroundFile = new File(backgroundSourceFilePath);
|
||||
// 2. 文件校验(文件不存在/不是文件 → 显示透明背景)
|
||||
File backgroundFile = new File(imagePath);
|
||||
if (!backgroundFile.exists() || !backgroundFile.isFile()) {
|
||||
LogUtils.e(TAG, "背景图片不存在:" + backgroundSourceFilePath);
|
||||
setDefaultImageViewBackground();
|
||||
LogUtils.e(TAG, "图片文件不存在或无效:" + imagePath);
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算图片原始宽高比
|
||||
// 3. 计算图片原始宽高比(确保不拉伸)
|
||||
if (!calculateImageAspectRatio(backgroundFile)) {
|
||||
setDefaultImageViewBackground();
|
||||
LogUtils.e(TAG, "图片尺寸无效,无法加载");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩加载 Bitmap(保持比例)
|
||||
// 4. 压缩加载Bitmap(避免OOM,保持原始比例)
|
||||
Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920);
|
||||
if (bitmap == null) {
|
||||
LogUtils.e(TAG, "图片加载失败,无法解析为 Bitmap");
|
||||
setDefaultImageViewBackground();
|
||||
LogUtils.e(TAG, "图片压缩加载失败:" + imagePath);
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置图片(保持ImageView透明背景)
|
||||
// 5. 设置图片到ImageView(保持透明背景)
|
||||
Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
ivBackground.setBackground(backgroundDrawable);
|
||||
@@ -285,43 +191,51 @@ public class BackgroundView extends RelativeLayout {
|
||||
ivBackground.setBackgroundDrawable(backgroundDrawable);
|
||||
}
|
||||
|
||||
// 调整ImageView尺寸(居中平铺,不拉伸)
|
||||
// 6. 调整ImageView尺寸(居中平铺,适配控件大小)
|
||||
adjustImageViewSize();
|
||||
LogUtils.d(TAG, "ImageView 背景加载成功(全透明+不拉伸),宽高比:" + imageAspectRatio);
|
||||
|
||||
LogUtils.d(TAG, "图片加载成功(" + (isPreviewMode ? "预览模式" : "正式模式") + ")");
|
||||
LogUtils.d(TAG, "图片路径:" + imagePath + ",宽高比:" + imageAspectRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图片原始宽高比(宽/高)
|
||||
* 计算图片原始宽高比(宽/高)→ 控制不拉伸的核心
|
||||
* @param file 图片文件(非空)
|
||||
* @return 成功:true,失败:false
|
||||
*/
|
||||
private boolean calculateImageAspectRatio(File file) {
|
||||
try {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
options.inJustDecodeBounds = true; // 仅获取尺寸,不加载图片(省内存)
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
|
||||
int imageWidth = options.outWidth;
|
||||
int imageHeight = options.outHeight;
|
||||
|
||||
// 校验尺寸有效性(宽/高必须大于0)
|
||||
if (imageWidth <= 0 || imageHeight <= 0) {
|
||||
LogUtils.e(TAG, "图片尺寸无效:宽=" + imageWidth + ", 高=" + imageHeight);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存原始宽高比
|
||||
imageAspectRatio = (float) imageWidth / imageHeight;
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "计算图片宽高比失败:" + e.getMessage());
|
||||
LogUtils.e(TAG, "计算图片宽高比失败:" + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整ImageView尺寸(不拉伸,居中平铺)
|
||||
* 调整ImageView尺寸(核心:不拉伸,居中平铺,适配控件大小)
|
||||
* 效果:ImageView按图片比例缩放,最大尺寸不超过控件,同时在控件中居中
|
||||
*/
|
||||
private void adjustImageViewSize() {
|
||||
int parentWidth = getWidth(); // 控件宽度(已填充父视图)
|
||||
int parentHeight = getHeight(); // 控件高度(已填充父视图)
|
||||
int parentWidth = getWidth(); // 控件宽度(已完全填充父视图)
|
||||
int parentHeight = getHeight(); // 控件高度(已完全填充父视图)
|
||||
|
||||
// 若父容器未测量完成(宽度/高度为0),延迟调整(避免尺寸计算错误)
|
||||
if (parentWidth == 0 || parentHeight == 0) {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
@@ -329,72 +243,86 @@ public class BackgroundView extends RelativeLayout {
|
||||
adjustImageViewSize();
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "父容器未测量完成,延迟调整ImageView尺寸");
|
||||
return;
|
||||
}
|
||||
|
||||
int imageViewWidth, imageViewHeight;
|
||||
if (imageAspectRatio >= 1.0f) { // 横图
|
||||
// 按图片原始比例,计算ImageView最大可行尺寸(不超过控件,不拉伸)
|
||||
if (imageAspectRatio >= 1.0f) { // 横图(宽 ≥ 高):优先适配控件宽度
|
||||
imageViewWidth = Math.min(parentWidth, (int) (parentHeight * imageAspectRatio));
|
||||
imageViewHeight = (int) (imageViewWidth / imageAspectRatio);
|
||||
} else { // 竖图
|
||||
} else { // 竖图(宽 < 高):优先适配控件高度
|
||||
imageViewHeight = Math.min(parentHeight, (int) (parentWidth / imageAspectRatio));
|
||||
imageViewWidth = (int) (imageViewHeight * imageAspectRatio);
|
||||
}
|
||||
|
||||
// 应用尺寸
|
||||
// 应用尺寸到ImageView(更新布局参数)
|
||||
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) ivBackground.getLayoutParams();
|
||||
layoutParams.width = imageViewWidth;
|
||||
layoutParams.height = imageViewHeight;
|
||||
ivBackground.setLayoutParams(layoutParams);
|
||||
|
||||
LogUtils.d(TAG, "ImageView 尺寸调整完成:宽=" + imageViewWidth + ", 高=" + imageViewHeight);
|
||||
LogUtils.d(TAG, "ImageView尺寸调整完成:宽=" + imageViewWidth + ", 高=" + imageViewHeight + "(控件尺寸:" + parentWidth + "x" + parentHeight + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 带压缩的 Bitmap 解码(保持比例,避免 OOM)
|
||||
* 带压缩的Bitmap解码(仅压缩大小,不改变原始比例,避免OOM)
|
||||
* @param file 图片文件
|
||||
* @param maxWidth 最大宽度(1080px,适配主流手机屏幕)
|
||||
* @param maxHeight 最大高度(1920px,适配主流手机屏幕)
|
||||
* @return 压缩后的Bitmap(null表示失败)
|
||||
*/
|
||||
private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) {
|
||||
try {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
options.inJustDecodeBounds = true; // 先获取图片尺寸
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
|
||||
// 计算压缩比例(仅缩小,不放大,保持比例)
|
||||
int scaleX = options.outWidth / maxWidth;
|
||||
int scaleY = options.outHeight / maxHeight;
|
||||
int inSampleSize = Math.max(scaleX, scaleY);
|
||||
if (inSampleSize <= 0) {
|
||||
inSampleSize = 1;
|
||||
inSampleSize = 1; // 最小压缩比例为1(不压缩)
|
||||
}
|
||||
|
||||
// 正式解码图片(压缩+省内存配置)
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = inSampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 比ARGB_8888省一半内存
|
||||
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "图片压缩加载失败:" + e.getMessage());
|
||||
LogUtils.e(TAG, "图片压缩加载失败:" + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认背景(全透明兜底,避免空白)
|
||||
* 设置默认透明背景(图片加载失败/路径无效时兜底)
|
||||
* 确保:无图片时控件完全透明,不遮挡下层视图
|
||||
*/
|
||||
private void setDefaultImageViewBackground() {
|
||||
// 关键:默认背景设为全透明(而非默认图,避免遮挡下层视图)
|
||||
private void setDefaultTransparentBackground() {
|
||||
ivBackground.setBackground(new ColorDrawable(0x00000000)); // 全透明背景
|
||||
imageAspectRatio = 1.0f;
|
||||
adjustImageViewSize();
|
||||
imageAspectRatio = 1.0f; // 重置宽高比(避免影响下次加载)
|
||||
adjustImageViewSize(); // 调整尺寸(确保ImageView居中且不占位异常)
|
||||
LogUtils.d(TAG, "已设置默认透明背景");
|
||||
}
|
||||
|
||||
/**
|
||||
* 父容器尺寸变化时(如屏幕旋转),重新调整ImageView尺寸
|
||||
* 确保:控件尺寸变化后,图片仍保持不拉伸+居中平铺
|
||||
*/
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
adjustImageViewSize();
|
||||
LogUtils.d(TAG, "控件尺寸变化:旧尺寸=" + oldw + "x" + oldh + ",新尺寸=" + w + "x" + h);
|
||||
adjustImageViewSize(); // 重新调整ImageView尺寸
|
||||
}
|
||||
|
||||
/**
|
||||
* 对外提供:判断当前是否处于预览模式
|
||||
* 【对外提供】判断当前是否处于预览模式
|
||||
* 用于:Activity中判断是否需要提交预览背景
|
||||
*/
|
||||
public boolean isPreviewMode() {
|
||||
return isPreviewMode;
|
||||
|
||||
Reference in New Issue
Block a user