Compare commits
16 Commits
powerbell-
...
powerbell-
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b69e04706 | |||
| a2a61bbf0b | |||
| 9f4211c83e | |||
| 447a786632 | |||
| ff0f239ffc | |||
| 7c59a982fc | |||
| 895cc4630d | |||
| 851a539364 | |||
| d79f2937ba | |||
| c524a21429 | |||
| a148de2ab8 | |||
| de34e823b6 | |||
| fd0833476e | |||
| 361b533b0d | |||
| f277f76468 | |||
| ec54865a8e |
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 10 17:52:12 HKT 2025
|
||||
stageCount=7
|
||||
#Thu Dec 11 03:04:35 HKT 2025
|
||||
stageCount=14
|
||||
libraryProject=
|
||||
baseVersion=15.12
|
||||
publishVersion=15.12.6
|
||||
publishVersion=15.12.13
|
||||
buildCount=0
|
||||
baseBetaVersion=15.12.7
|
||||
baseBetaVersion=15.12.14
|
||||
|
||||
@@ -2,16 +2,21 @@ package cc.winboll.studio.powerbell;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import cc.winboll.studio.powerbell.receivers.GlobalApplicationReceiver;
|
||||
import cc.winboll.studio.powerbell.utils.AppCacheUtils;
|
||||
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
||||
import cc.winboll.studio.powerbell.utils.BitmapCacheUtils;
|
||||
import java.io.File;
|
||||
|
||||
public class App extends GlobalApplication {
|
||||
|
||||
public static final String TAG = "App";
|
||||
|
||||
public static final String COMPONENT_EN1 = "cc.winboll.studio.powerbell.MainActivityEN1";
|
||||
@@ -24,6 +29,9 @@ public class App extends GlobalApplication {
|
||||
// 数据配置存储工具
|
||||
static AppConfigUtils _mAppConfigUtils;
|
||||
static AppCacheUtils _mAppCacheUtils;
|
||||
// 新增:全局 Bitmap 缓存工具(常驻内存)
|
||||
public static BitmapCacheUtils _mBitmapCacheUtils;
|
||||
|
||||
GlobalApplicationReceiver mReceiver;
|
||||
static String szTempDir = "";
|
||||
|
||||
@@ -35,24 +43,15 @@ public class App extends GlobalApplication {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
//setIsDebugging(false);
|
||||
|
||||
// 初始化活动窗口管理
|
||||
WinBoLLActivityManager.init(this);
|
||||
// 初始化 Toast 框架
|
||||
ToastUtils.init(this);
|
||||
|
||||
// 临时文件夹方案1
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
// 临时文件夹初始化(保持原有逻辑)
|
||||
File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
// 定义目标文件路径(在Pictures目录下创建"PowerBell"子文件夹及文件)
|
||||
File powerBellDir = new File(picturesDir, "PowerBell");
|
||||
|
||||
// 临时文件夹方案2 <图片保存失败>
|
||||
// 获取Pictures文件夹路径(Android 10及以上推荐使用MediaStore,此处为传统方式)
|
||||
//File powerBellDir = getExternalFilesDir("TempDir");
|
||||
|
||||
// 先创建文件夹(如果不存在)
|
||||
if (!powerBellDir.exists()) {
|
||||
powerBellDir.mkdirs();
|
||||
}
|
||||
@@ -61,11 +60,54 @@ public class App extends GlobalApplication {
|
||||
// 设置数据配置存储工具
|
||||
_mAppConfigUtils = getAppConfigUtils(this);
|
||||
_mAppCacheUtils = getAppCacheUtils(this);
|
||||
// 初始化全局 Bitmap 缓存工具(关键:App 启动时初始化,常驻内存)
|
||||
_mBitmapCacheUtils = BitmapCacheUtils.getInstance();
|
||||
|
||||
mReceiver = new GlobalApplicationReceiver(this);
|
||||
mReceiver.registerAction();
|
||||
|
||||
// ======================== 新增:异步预加载背景图 ========================
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// 1. 获取背景源工具类实例
|
||||
BackgroundSourceUtils bgSourceUtils = BackgroundSourceUtils.getInstance(App.this);
|
||||
if (bgSourceUtils == null) {
|
||||
LogUtils.e(TAG, "preloadBitmap: BackgroundSourceUtils 实例为空");
|
||||
return;
|
||||
}
|
||||
// 2. 获取当前背景Bean
|
||||
BackgroundBean bgBean = bgSourceUtils.getCurrentBackgroundBean();
|
||||
if (bgBean == null || !bgBean.isUseBackgroundFile()) {
|
||||
LogUtils.d(TAG, "preloadBitmap: 无有效背景文件,跳过预加载");
|
||||
return;
|
||||
}
|
||||
// 3. 获取背景图路径(优先取压缩图路径)
|
||||
String bgPath = bgBean.isUseBackgroundScaledCompressFile()
|
||||
? bgBean.getBackgroundScaledCompressFilePath()
|
||||
: bgBean.getBackgroundFilePath();
|
||||
// 4. 预加载到全局缓存
|
||||
if (_mBitmapCacheUtils != null) {
|
||||
_mBitmapCacheUtils.cacheBitmap(bgPath);
|
||||
LogUtils.d(TAG, "preloadBitmap: 应用启动时预加载成功 - " + bgPath);
|
||||
} else {
|
||||
LogUtils.e(TAG, "preloadBitmap: 全局 BitmapCacheUtils 未初始化");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "preloadBitmap: 预加载失败 - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}, 1000); // 延迟1秒执行,避免阻塞应用初始化
|
||||
// ======================== 预加载逻辑结束 ========================
|
||||
}
|
||||
|
||||
// 保持原有方法不变
|
||||
public static AppConfigUtils getAppConfigUtils(Context context) {
|
||||
if (_mAppConfigUtils == null) {
|
||||
_mAppConfigUtils = AppConfigUtils.getInstance(context);
|
||||
@@ -88,8 +130,10 @@ public class App extends GlobalApplication {
|
||||
public void onTerminate() {
|
||||
super.onTerminate();
|
||||
ToastUtils.release();
|
||||
// 可选:App 终止时清空 Bitmap 缓存,释放内存
|
||||
if (_mBitmapCacheUtils != null) {
|
||||
_mBitmapCacheUtils.clearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -14,6 +15,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.FileProvider;
|
||||
@@ -74,17 +76,17 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mBgSourceUtils.loadSettings();
|
||||
mPermissionUtils = PermissionUtils.getInstance();
|
||||
|
||||
// File tempDir = new File(App.getTempDirPath());
|
||||
// if (!tempDir.exists()) {
|
||||
// tempDir.mkdirs();
|
||||
// }
|
||||
// mfTakePhoto = new File(tempDir, "TakePhoto.jpg");
|
||||
//
|
||||
// File selectTempDir = new File(mBgSourceUtils.getBackgroundSourceDirPath(), "SelectTemp");
|
||||
// if (!selectTempDir.exists()) {
|
||||
// selectTempDir.mkdirs();
|
||||
// LogUtils.d(TAG, "【选图初始化】选图临时目录创建完成:" + selectTempDir.getAbsolutePath());
|
||||
// }
|
||||
File tempDir = new File(App.getTempDirPath());
|
||||
if (!tempDir.exists()) {
|
||||
tempDir.mkdirs();
|
||||
}
|
||||
mfTakePhoto = new File(tempDir, "TakePhoto.jpg");
|
||||
|
||||
File selectTempDir = new File(mBgSourceUtils.getBackgroundSourceDirPath(), "SelectTemp");
|
||||
if (!selectTempDir.exists()) {
|
||||
selectTempDir.mkdirs();
|
||||
LogUtils.d(TAG, "【选图初始化】选图临时目录创建完成:" + selectTempDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
initToolbar();
|
||||
initClickListeners();
|
||||
@@ -255,8 +257,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
REQUEST_CROP_IMAGE
|
||||
);
|
||||
REQUEST_CROP_IMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -270,8 +271,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
0,
|
||||
0,
|
||||
true,
|
||||
REQUEST_CROP_IMAGE
|
||||
);
|
||||
REQUEST_CROP_IMAGE);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -429,8 +429,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
REQUEST_CROP_IMAGE
|
||||
);
|
||||
REQUEST_CROP_IMAGE);
|
||||
LogUtils.d(TAG, "【拍照完成】已启动裁剪");
|
||||
}
|
||||
|
||||
@@ -499,8 +498,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
mBackgroundView.getWidth(),
|
||||
mBackgroundView.getHeight(),
|
||||
false,
|
||||
REQUEST_CROP_IMAGE
|
||||
);
|
||||
REQUEST_CROP_IMAGE);
|
||||
}
|
||||
|
||||
boolean putUriFileToPreviewSource(Uri srcUriFile) {
|
||||
@@ -514,6 +512,9 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
return FileUtils.copyFile(srcFile, dstFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心修改:裁剪成功后调用双重刷新,确保最新图片加载
|
||||
*/
|
||||
private void handleCropImageResult(int requestCode, int resultCode, Intent data) {
|
||||
LogUtils.d(TAG, "handleCropImageResult: 处理裁剪结果");
|
||||
File cropTempFile = new File(mBgSourceUtils.getPreviewBackgroundBean().getBackgroundScaledCompressFilePath());
|
||||
@@ -525,7 +526,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
if (isCropSuccess) {
|
||||
isPreviewBackgroundChanged = true;
|
||||
LogUtils.d(TAG, "handleCropImageResult: 裁剪成功");
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
final BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
previewBean.setIsUseBackgroundFile(true);
|
||||
previewBean.setIsUseBackgroundScaledCompressFile(true);
|
||||
mBgSourceUtils.saveSettings();
|
||||
@@ -546,15 +547,16 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
}
|
||||
}
|
||||
|
||||
// 关键修改:延迟300ms调用双重刷新,确保文件写入完成
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mBackgroundView != null && !isFinishing()) {
|
||||
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||
LogUtils.d(TAG, "handleCropImageResult: 裁剪图片加载完成");
|
||||
if (!isFinishing()) {
|
||||
doubleRefreshPreview();
|
||||
LogUtils.d(TAG, "handleCropImageResult: 触发双重刷新");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}, 300);
|
||||
} else {
|
||||
handleOperationCancelOrFail();
|
||||
}
|
||||
@@ -697,8 +699,40 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
return cropBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心修改:添加缓存清理 + 控件 Bitmap 清空逻辑
|
||||
*/
|
||||
private void doubleRefreshPreview() {
|
||||
LogUtils.d(TAG, "【双重刷新】开始");
|
||||
// 1. 清空缓存工具类的旧数据
|
||||
if (mBgSourceUtils != null) {
|
||||
BackgroundBean previewBean = mBgSourceUtils.getPreviewBackgroundBean();
|
||||
if (previewBean != null && previewBean.isUseBackgroundFile()) {
|
||||
String bgPath = previewBean.isUseBackgroundScaledCompressFile()
|
||||
? previewBean.getBackgroundScaledCompressFilePath()
|
||||
: previewBean.getBackgroundFilePath();
|
||||
// 强制清除缓存
|
||||
App._mBitmapCacheUtils.removeCachedBitmap(bgPath);
|
||||
LogUtils.d(TAG, "【双重刷新】已清空工具类缓存:" + bgPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 清空 BackgroundView 自身的 Bitmap 引用
|
||||
// if (mBackgroundView != null) {
|
||||
// // 清空 ImageView 持有的 Bitmap
|
||||
// if (mBackgroundView instanceof BackgroundView) {
|
||||
// ((BackgroundView) mBackgroundView).setImageBitmap(null);
|
||||
// }
|
||||
// // 清空背景 Drawable 引用
|
||||
// Drawable drawable = mBackgroundView.getBackground();
|
||||
// if (drawable != null) {
|
||||
// drawable.setCallback(null);
|
||||
// mBackgroundView.setBackground(null);
|
||||
// }
|
||||
// LogUtils.d(TAG, "【双重刷新】已清空控件 Bitmap 引用");
|
||||
// }
|
||||
|
||||
// 3. 重新加载最新数据
|
||||
if (mBackgroundView != null && !isFinishing()) {
|
||||
mBgSourceUtils.loadSettings();
|
||||
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||
@@ -708,6 +742,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 延长延迟到200ms,确保文件完全加载
|
||||
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -717,7 +752,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
LogUtils.d(TAG, "【双重刷新】第二重完成");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
|
||||
@@ -728,27 +763,6 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
doubleRefreshPreview();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启动系统裁剪工具
|
||||
* @param activity 上下文
|
||||
* @param srcFile 裁剪原图
|
||||
* @param aspectX 裁剪宽比例(自由裁剪传0)
|
||||
* @param aspectY 裁剪高比例(自由裁剪传0)
|
||||
* @param isFreeCrop 是否自由裁剪
|
||||
* @param requestCode 裁剪请求码
|
||||
*/
|
||||
/**
|
||||
* 启动系统裁剪工具
|
||||
* @param activity 上下文
|
||||
* @param srcFile 裁剪原图
|
||||
* @param aspectX 裁剪宽比例(自由裁剪传0)
|
||||
* @param aspectY 裁剪高比例(自由裁剪传0)
|
||||
* @param isFreeCrop 是否自由裁剪
|
||||
* @param requestCode 裁剪请求码
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 获取FileProvider Uri(复用方法,避免重复代码)
|
||||
*/
|
||||
@@ -781,7 +795,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
||||
} else {
|
||||
// 如果预览背景改变过就提示是否更换背景
|
||||
if (isPreviewBackgroundChanged) {
|
||||
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener(){
|
||||
YesNoAlertDialog.show(this, "背景更换问题", "是否确定背景图片设置?", new YesNoAlertDialog.OnDialogResultListener() {
|
||||
@Override
|
||||
public void onYes() {
|
||||
mBgSourceUtils.commitPreviewSourceToCurrent();
|
||||
|
||||
@@ -4,13 +4,14 @@ import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2024/07/18 11:52:28
|
||||
* @Describe 应用背景图片数据类(存储正式/预览背景配置,支持JSON序列化/反序列化)
|
||||
*/
|
||||
public class BackgroundBean extends BaseBean {
|
||||
public class BackgroundBean extends BaseBean implements Serializable {
|
||||
|
||||
public static final String TAG = "BackgroundPictureBean";
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package cc.winboll.studio.powerbell.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/12/11 01:57
|
||||
* @Describe 单例 Bitmap 缓存工具类(Java 7 兼容)
|
||||
* 功能:内存缓存 Bitmap,支持路径关联缓存、全局获取、缓存清空
|
||||
* 特点:1. 单例模式 2. 压缩加载避免OOM 3. 路径- Bitmap 映射 4. 线程安全
|
||||
*/
|
||||
public class BitmapCacheUtils {
|
||||
private static final String TAG = "BitmapCacheUtils";
|
||||
// 最大图片尺寸(适配1080P屏幕,可根据需求调整)
|
||||
private static final int MAX_WIDTH = 1080;
|
||||
private static final int MAX_HEIGHT = 1920;
|
||||
|
||||
// 单例实例(volatile 保证多线程可见性)
|
||||
private static volatile BitmapCacheUtils sInstance;
|
||||
// 路径-Bitmap 缓存容器(内存缓存)
|
||||
private final Map<String, Bitmap> mBitmapCacheMap;
|
||||
|
||||
// 私有构造器(单例模式)
|
||||
private BitmapCacheUtils() {
|
||||
mBitmapCacheMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例(双重校验锁,线程安全)
|
||||
*/
|
||||
public static BitmapCacheUtils getInstance() {
|
||||
if (sInstance == null) {
|
||||
synchronized (BitmapCacheUtils.class) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new BitmapCacheUtils();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心接口:根据图片路径缓存 Bitmap 到内存
|
||||
* @param imagePath 图片绝对路径
|
||||
* @return 缓存成功的 Bitmap / null(路径无效/文件不存在)
|
||||
*/
|
||||
public Bitmap cacheBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片路径为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
File imageFile = new File(imagePath);
|
||||
if (!imageFile.exists() || !imageFile.isFile()) {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片文件不存在 - " + imagePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 已缓存则直接返回,避免重复加载
|
||||
if (mBitmapCacheMap.containsKey(imagePath)) {
|
||||
LogUtils.d(TAG, "cacheBitmap: 图片已缓存,直接返回 - " + imagePath);
|
||||
return mBitmapCacheMap.get(imagePath);
|
||||
}
|
||||
|
||||
// 压缩加载 Bitmap(避免OOM)
|
||||
Bitmap bitmap = decodeCompressedBitmap(imagePath);
|
||||
if (bitmap != null) {
|
||||
// 存入缓存容器
|
||||
mBitmapCacheMap.put(imagePath, bitmap);
|
||||
LogUtils.d(TAG, "cacheBitmap: 图片缓存成功 - " + imagePath);
|
||||
} else {
|
||||
LogUtils.e(TAG, "cacheBitmap: 图片解码失败 - " + imagePath);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心接口:根据路径获取缓存的 Bitmap
|
||||
* @param imagePath 图片绝对路径
|
||||
* @return 缓存的 Bitmap / null
|
||||
*/
|
||||
public Bitmap getCachedBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
return null;
|
||||
}
|
||||
return mBitmapCacheMap.get(imagePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有 Bitmap 缓存(释放内存)
|
||||
*/
|
||||
public void clearAllCache() {
|
||||
for (Bitmap bitmap : mBitmapCacheMap.values()) {
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle(); // 主动回收 Bitmap
|
||||
}
|
||||
}
|
||||
mBitmapCacheMap.clear();
|
||||
LogUtils.d(TAG, "clearAllCache: 所有 Bitmap 缓存已清空");
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定路径的 Bitmap 缓存
|
||||
* @param imagePath 图片绝对路径
|
||||
*/
|
||||
public void removeCachedBitmap(String imagePath) {
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = mBitmapCacheMap.remove(imagePath);
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
bitmap.recycle();
|
||||
LogUtils.d(TAG, "removeCachedBitmap: 移除并回收缓存 - " + imagePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩解码 Bitmap(按最大尺寸缩放,避免OOM)
|
||||
*/
|
||||
private Bitmap decodeCompressedBitmap(String imagePath) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
// 第一步:只获取图片尺寸,不加载像素
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
// 计算缩放比例
|
||||
int sampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);
|
||||
|
||||
// 第二步:加载压缩后的 Bitmap
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存(比ARGB_8888少一半内存)
|
||||
options.inPurgeable = true;
|
||||
options.inInputShareable = true;
|
||||
|
||||
return BitmapFactory.decodeFile(imagePath, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 Bitmap 缩放比例
|
||||
*/
|
||||
private int calculateInSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
|
||||
int rawWidth = options.outWidth;
|
||||
int rawHeight = options.outHeight;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (rawWidth > maxWidth || rawHeight > maxHeight) {
|
||||
int halfWidth = rawWidth / 2;
|
||||
int halfHeight = rawHeight / 2;
|
||||
while ((halfWidth / inSampleSize) >= maxWidth && (halfHeight / inSampleSize) >= maxHeight) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.widget.ImageView.ScaleType;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.powerbell.App;
|
||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||
import java.io.File;
|
||||
|
||||
@@ -22,6 +24,8 @@ import java.io.File;
|
||||
public class BackgroundView extends RelativeLayout {
|
||||
|
||||
public static final String TAG = "BackgroundView";
|
||||
// 新增:记录当前已缓存的图片路径
|
||||
private String mCurrentCachedPath = "";
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mLlContainer; // 主容器LinearLayout
|
||||
@@ -55,8 +59,6 @@ public class BackgroundView extends RelativeLayout {
|
||||
LogUtils.d(TAG, "=== initView 启动 ===");
|
||||
// 1. 配置当前控件:全屏+透明
|
||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
//setBackgroundColor(0x00000000);
|
||||
//setBackground(new ColorDrawable(0x00000000));
|
||||
|
||||
// 2. 初始化主容器LinearLayout
|
||||
initLinearLayout();
|
||||
@@ -106,17 +108,16 @@ public class BackgroundView extends RelativeLayout {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
if (bean.isUseBackgroundScaledCompressFile()) {
|
||||
loadImage(bean.getBackgroundScaledCompressFilePath());
|
||||
} else {
|
||||
|
||||
loadImage(bean.getBackgroundFilePath());
|
||||
}
|
||||
String targetPath = bean.isUseBackgroundScaledCompressFile()
|
||||
? bean.getBackgroundScaledCompressFilePath()
|
||||
: bean.getBackgroundFilePath();
|
||||
// 调用带路径判断的loadImage方法
|
||||
loadImage(targetPath);
|
||||
}
|
||||
|
||||
// ====================================== 对外方法 ======================================
|
||||
/**
|
||||
* 加载图片(保持原图比例,在LinearLayout中居中平铺)
|
||||
* 改造后:添加路径判断,路径更新时同步更新缓存
|
||||
* @param imagePath 图片绝对路径
|
||||
*/
|
||||
public void loadImage(String imagePath) {
|
||||
@@ -135,22 +136,48 @@ public class BackgroundView extends RelativeLayout {
|
||||
|
||||
mIvBackground.setVisibility(View.GONE);
|
||||
|
||||
// 计算原图比例
|
||||
// ======================== 新增:路径判断逻辑 ========================
|
||||
// 1. 路径未变化:直接使用缓存
|
||||
if (imagePath.equals(mCurrentCachedPath)) {
|
||||
//ToastUtils.show("isReload == false");
|
||||
Bitmap cachedBitmap = App._mBitmapCacheUtils.getCachedBitmap(imagePath);
|
||||
if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
|
||||
LogUtils.d(TAG, "loadImage: 路径未变,使用缓存 Bitmap");
|
||||
mImageAspectRatio = (float) cachedBitmap.getWidth() / cachedBitmap.getHeight();
|
||||
mIvBackground.setImageBitmap(cachedBitmap);
|
||||
adjustImageViewSize();
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 路径已更新:移除旧缓存,加载新图片并更新缓存
|
||||
if (!TextUtils.isEmpty(mCurrentCachedPath)) {
|
||||
App._mBitmapCacheUtils.removeCachedBitmap(mCurrentCachedPath);
|
||||
LogUtils.d(TAG, "loadImage: 路径已更新,移除旧缓存 - " + mCurrentCachedPath);
|
||||
}
|
||||
// ======================== 路径判断逻辑结束 ========================
|
||||
|
||||
// 无缓存/路径更新:走原有逻辑加载图片
|
||||
if (!calculateImageAspectRatio(imageFile)) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 压缩加载Bitmap
|
||||
Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920);
|
||||
if (bitmap == null) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置图片
|
||||
// 缓存新图片,并更新当前缓存路径记录
|
||||
App._mBitmapCacheUtils.cacheBitmap(imagePath);
|
||||
mCurrentCachedPath = imagePath;
|
||||
LogUtils.d(TAG, "loadImage: 加载新图片并更新缓存 - " + imagePath);
|
||||
|
||||
mIvBackground.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
|
||||
adjustImageViewSize(); // 调整尺寸
|
||||
adjustImageViewSize();
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
LogUtils.d(TAG, "=== loadImage 完成 ===");
|
||||
}
|
||||
|
||||
@@ -198,30 +225,15 @@ public class BackgroundView extends RelativeLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整ImageView尺寸(保持原图比例,在LinearLayout中居中平铺)
|
||||
*/
|
||||
private void adjustImageViewSize() {
|
||||
//LogUtils.d(TAG, "=== adjustImageViewSize 启动 ===");
|
||||
if (mLlContainer == null || mIvBackground == null) {
|
||||
//LogUtils.e(TAG, "控件为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取LinearLayout尺寸
|
||||
int llWidth = mLlContainer.getWidth();
|
||||
int llHeight = mLlContainer.getHeight();
|
||||
if (llWidth == 0 || llHeight == 0) {
|
||||
postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adjustImageViewSize();
|
||||
}
|
||||
}, 120);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算ImageView尺寸(保持比例,不超出LinearLayout)
|
||||
if (llWidth != 0 && llHeight != 0) {
|
||||
int ivWidth, ivHeight;
|
||||
if (mImageAspectRatio >= 1.0f) {
|
||||
ivWidth = Math.min((int) (llHeight * mImageAspectRatio), llWidth);
|
||||
@@ -231,23 +243,21 @@ public class BackgroundView extends RelativeLayout {
|
||||
ivWidth = (int) (ivHeight * mImageAspectRatio);
|
||||
}
|
||||
|
||||
// 应用尺寸
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mIvBackground.getLayoutParams();
|
||||
params.width = ivWidth;
|
||||
params.height = ivHeight;
|
||||
mIvBackground.setLayoutParams(params);
|
||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 确保居中平铺
|
||||
mIvBackground.setScaleType(ScaleType.FIT_CENTER);
|
||||
mIvBackground.setVisibility(View.VISIBLE);
|
||||
|
||||
|
||||
//LogUtils.d(TAG, "ImageView尺寸:" + ivWidth + "x" + ivHeight);
|
||||
//LogUtils.d(TAG, "=== adjustImageViewSize 完成 ===");
|
||||
}
|
||||
}
|
||||
|
||||
private void setDefaultTransparentBackground() {
|
||||
mIvBackground.setImageBitmap(null);
|
||||
mIvBackground.setBackgroundColor(0x00000000);
|
||||
mImageAspectRatio = 1.0f;
|
||||
// 清空缓存路径记录
|
||||
mCurrentCachedPath = "";
|
||||
}
|
||||
|
||||
// ====================================== 重写方法 ======================================
|
||||
@@ -257,3 +267,4 @@ public class BackgroundView extends RelativeLayout {
|
||||
adjustImageViewSize(); // 尺寸变化时重新调整
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 顶部Toolbar(首屏核心,同步加载,保留原有ASupportToolbar) -->
|
||||
<cc.winboll.studio.libaes.views.ASupportToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/toolbar_height"
|
||||
@@ -13,35 +14,31 @@
|
||||
android:gravity="center_vertical"
|
||||
style="@style/DefaultAToolbar"/>
|
||||
|
||||
<!-- 主内容区(优化层级,减少冗余RelativeLayout) -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<!-- 首屏核心容器(合并原冗余RelativeLayout,减少层级) -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/activitymainRelativeLayout1"/>
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:id="@+id/activitymainRelativeLayout1">
|
||||
|
||||
<!-- 1. 背景视图(首屏核心,同步加载,保留原有) -->
|
||||
<cc.winboll.studio.powerbell.views.BackgroundView
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FF7381FF"
|
||||
android:id="@+id/fragmentmainviewBackgroundView1"/>
|
||||
|
||||
<!-- 2. 功能控件容器(首屏核心,同步加载,保留原有结构) -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- 服务总开关布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
@@ -70,6 +67,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 电量控制核心布局(SeekBar+图标) -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
@@ -84,6 +82,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<!-- 耗电提醒布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
@@ -117,6 +116,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 耗电提醒数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
@@ -139,6 +139,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 当前电量数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -162,6 +163,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 充电提醒数值+图标 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="80dp"
|
||||
@@ -184,6 +186,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 充电提醒布局 -->
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="50dp"
|
||||
@@ -221,6 +224,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Tips文本 -->
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent">
|
||||
@@ -238,13 +242,16 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<cc.winboll.studio.libaes.views.ADsBannerView
|
||||
<!-- 3. 广告视图:关键优化→用ViewStub延迟加载(替代原直接加载的ADsBannerView) -->
|
||||
<!-- 首次启动仅占位(1px),不inflate真实广告视图,减少首次耗时 -->
|
||||
<ViewStub
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/adsbanner"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
android:id="@+id/stub_ads_banner"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout="@layout/view_ads_banner"/> <!-- 广告视图独立布局文件 -->
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
7
powerbell/src/main/res/layout/view_ads_banner.xml
Normal file
7
powerbell/src/main/res/layout/view_ads_banner.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 广告视图独立布局(供ViewStub延迟加载) -->
|
||||
<cc.winboll.studio.libaes.views.ADsBannerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/adsbanner"/>
|
||||
Reference in New Issue
Block a user