From 82518af2d6cde5a49ebd3af59843754470f1aa46 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Thu, 20 Nov 2025 20:16:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=A4=BA=E4=BE=8B=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=8E=A7=E4=BB=B6=E7=9A=84=E5=BC=95=E7=94=A8=E4=B8=8E?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E6=95=B0=E6=8D=AE=E5=AD=98=E5=8F=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- powerbell/build.properties | 4 +- .../dialogs/NetworkBackgroundDialog.java | 12 +- .../unittest/BackgroundViewTestFragment.java | 12 +- .../powerbell/unittest/BaseTestFragment.java | 34 --- .../powerbell/views/BackgroundView.java | 258 +++++++++++++++--- .../main/res/drawable/default_background.xml | 170 ++++++++++++ .../main/res/layout/activity_mainunittest.xml | 7 +- .../res/layout/dialog_networkbackground.xml | 9 +- .../layout/fragment_test_backgroundview.xml | 10 +- .../src/main/res/layout/view_background.xml | 12 - 10 files changed, 426 insertions(+), 102 deletions(-) delete mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BaseTestFragment.java create mode 100644 powerbell/src/main/res/drawable/default_background.xml delete mode 100644 powerbell/src/main/res/layout/view_background.xml diff --git a/powerbell/build.properties b/powerbell/build.properties index 625cf00b..5ada7c58 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Nov 20 03:22:58 GMT 2025 +#Thu Nov 20 12:14:50 GMT 2025 stageCount=4 libraryProject= baseVersion=15.11 publishVersion=15.11.3 -buildCount=34 +buildCount=51 baseBetaVersion=15.11.4 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java index 8a93a390..698323ed 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/NetworkBackgroundDialog.java @@ -16,6 +16,7 @@ import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.utils.ImageDownloader; import java.io.FileInputStream; import java.io.IOException; +import cc.winboll.studio.powerbell.views.BackgroundView; /** * @Author ZhanGSKen&豆包大模型 @@ -33,8 +34,7 @@ public class NetworkBackgroundDialog extends AlertDialog { private Button btnConfirm; private Button btnPreview; private EditText etURL; - //private WebView webViewPreview; - ImageView ivBackgroundPreview; + BackgroundView bvBackgroundPreview; Context mContext; // 按钮点击回调接口(Java7 接口实现) @@ -77,7 +77,7 @@ public class NetworkBackgroundDialog extends AlertDialog { btnPreview = (Button) dialogView.findViewById(R.id.btn_preview); etURL = (EditText) dialogView.findViewById(R.id.et_url); //webViewPreview = (WebView) dialogView.findViewById(R.id.webview_preview); - ivBackgroundPreview = (ImageView) findViewById(R.id.iv_background_preview); + bvBackgroundPreview = (BackgroundView) findViewById(R.id.bv_background_preview); // 设置按钮点击事件 setButtonClickListeners(); @@ -163,14 +163,14 @@ public class NetworkBackgroundDialog extends AlertDialog { Drawable drawable = Drawable.createFromStream(fis, null); // 设置背景 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { - ivBackgroundPreview.setBackground(drawable); + bvBackgroundPreview.setBackground(drawable); } else { - ivBackgroundPreview.setBackgroundDrawable(drawable); + bvBackgroundPreview.setBackgroundDrawable(drawable); } } catch (Exception e) { e.printStackTrace(); //ivBackgroundPreview.setBackgroundResource(R.drawable.default_error); // 异常时显示默认图 - ivBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher); // 异常时显示默认图 + bvBackgroundPreview.setBackgroundResource(R.drawable.ic_launcher); // 异常时显示默认图 } finally { // Java7 手动关闭流,避免资源泄漏 if (fis != null) { diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BackgroundViewTestFragment.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BackgroundViewTestFragment.java index 19f0f6ad..ac038ca8 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BackgroundViewTestFragment.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BackgroundViewTestFragment.java @@ -5,6 +5,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.fragment.app.Fragment; +import cc.winboll.studio.libappbase.GlobalApplication; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; @@ -21,11 +22,16 @@ public class BackgroundViewTestFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); + //super.onCreateView(inflater, container, savedInstanceState); + + // 非调试状态就结束本线程 + if (!GlobalApplication.isDebugging()) { + Thread.currentThread().destroy(); + } + mainView = inflater.inflate(R.layout.fragment_test_backgroundview, container, false); - + ToastUtils.show(String.format("%s onCreate", TAG)); - return mainView; } } diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BaseTestFragment.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BaseTestFragment.java deleted file mode 100644 index 6523ee5e..00000000 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/unittest/BaseTestFragment.java +++ /dev/null @@ -1,34 +0,0 @@ -package cc.winboll.studio.powerbell.unittest; - -/** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/11/19 18:56 - * @Describe BaseTestFragment - */ -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.fragment.app.Fragment; -import cc.winboll.studio.libappbase.GlobalApplication; -import cc.winboll.studio.libappbase.ToastUtils; - -public class BaseTestFragment extends Fragment { - - public static final String TAG = "BaseTestFragment"; - - View mainView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - // 非调试状态就结束本线程 - if (!GlobalApplication.isDebugging()) { - Thread.currentThread().destroy(); - } - - ToastUtils.show(String.format("%s onCreate", TAG)); - - return null; - } -} diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java index d1b2bd32..924227ce 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/BackgroundView.java @@ -1,50 +1,240 @@ package cc.winboll.studio.powerbell.views; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; -import android.widget.LinearLayout; +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; /** * @Author ZhanGSKen&豆包大模型 * @Date 2025/11/19 18:01 - * @Describe 背景图片视图控件 + * @Describe 背景图片视图控件(加载外置存储 Background/current.jpg) */ -public class BackgroundView extends LinearLayout { +public class BackgroundView extends RelativeLayout { public static final String TAG = "BackgroundView"; - Context mContext; - View mMianView; - - public BackgroundView(Context context) { - super(context); - this.mContext = context; - initView(); - } - - public BackgroundView(Context context, AttributeSet attrs) { - super(context, attrs); - this.mContext = context; - initView(); - } - - public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - this.mContext = context; - initView(); - } - - public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - this.mContext = context; - initView(); - } - - void initView() { - this.mMianView = inflate(this.mContext, R.layout.view_background, null); - addView(this.mMianView); - } + Context mContext; + View mMianView; + // 外置存储背景图片路径:/storage/emulated/0/Android/data/应用包名/files/Background/current.jpg + private static String BACKGROUND_IMAGE_FOLDER = "Background"; + private static String BACKGROUND_IMAGE_FILENAME = "current.data"; + private static String backgroundSourceFilePath; + + public BackgroundView(Context context) { + super(context); + this.mContext = context; + initView(); + } + + public BackgroundView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + initView(); + } + + public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.mContext = context; + initView(); + } + + public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + this.mContext = context; + initView(); + } + + void initView() { + // 1. 初始化外置存储背景图片路径(应用私有外置存储,无需动态权限) + initBackgroundImagePath(); + + // 2. 加载并设置背景图片 + loadAndSetBackground(); + } + + /** + * 初始化外置存储背景图片路径 + * 路径:/storage/emulated/0/Android/data/应用包名/files/Background/current.jpg + */ + private void initBackgroundImagePath() { + // 获取应用私有外置存储的 files 目录(API 19+ 无需权限) + File externalFilesDir = mContext.getExternalFilesDir(null); + if (externalFilesDir == null) { + LogUtils.e(TAG, "外置存储不可用,无法初始化背景图片路径"); + return; + } + + // 拼接 Background 目录和 current.jpg 文件路径 + File backgroundDir = new File(externalFilesDir, "Background"); + if(!backgroundDir.exists()){ + backgroundDir.mkdirs(); + } + //BACKGROUND_IMAGE_PATH = new File(backgroundDir, "current.jpg").getAbsolutePath(); + //BACKGROUND_IMAGE_PATH = new File(backgroundDir, "current.data").getAbsolutePath(); + backgroundSourceFilePath = new File(backgroundDir, BACKGROUND_IMAGE_FILENAME).getAbsolutePath(); + //LogUtils.d(TAG, "背景图片路径:" + BACKGROUND_IMAGE_PATH); + } + + /** + * 拷贝图片文件到背景资源目录 + * @param srcBackgroundPath 源文件路径(待拷贝的图片路径) + */ + public void saveToBackgroundSources(String srcBackgroundPath) { + // 1. 初始化目标路径(确保路径有效) + initBackgroundImagePath(); + if (backgroundSourceFilePath == null) { + LogUtils.e(TAG, "目标路径初始化失败,无法保存背景图片"); + return; + } + + // 2. 校验源文件 + File srcFile = new File(srcBackgroundPath); + if (!srcFile.exists() || !srcFile.isFile()) { + LogUtils.e(TAG, String.format("源文件不存在或不是文件:%s", srcBackgroundPath)); + return; + } + + // 3. 创建目标目录(若不存在) + 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; + } + } + + // 4. 文件流拷贝(Java 7 原生实现,兼容低版本) + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(srcFile); + fos = new FileOutputStream(destFile); // 覆盖已有文件(如需询问用户可在此处添加逻辑) + + // 缓冲区拷贝(提升效率,避免频繁 IO 操作) + 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)); + + } catch (Exception e) { + // 捕获所有异常,避免崩溃 + LogUtils.e(TAG, String.format("文件拷贝失败:%s", e.getMessage()), e); + // 拷贝失败时删除目标文件(避免生成损坏文件) + if (destFile.exists()) { + destFile.delete(); + LogUtils.d(TAG, "已删除损坏的目标文件"); + } + } finally { + // 5. 关闭流(Java 7 手动关闭,避免资源泄漏) + 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()); + } + } + } + } + + /** + * 加载外置存储的 current.jpg 并设置为控件背景 + * 支持:文件压缩、版本兼容、异常兜底 + */ + private void loadAndSetBackground() { + // 校验路径是否有效 + if (backgroundSourceFilePath == null) { + setDefaultBackground(); + return; + } + + File backgroundFile = new File(backgroundSourceFilePath); + // 校验文件是否存在 + if (!backgroundFile.exists() || !backgroundFile.isFile()) { + LogUtils.e(TAG, "背景图片不存在:" + backgroundSourceFilePath); + setDefaultBackground(); + return; + } + + // 3. 压缩加载 Bitmap(避免 OOM) + Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920); // 最大宽高限制 + if (bitmap == null) { + LogUtils.e(TAG, "图片加载失败,无法解析为 Bitmap"); + setDefaultBackground(); + return; + } + + // 4. 设置为控件背景(兼容 Android 低版本) + Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + this.setBackground(backgroundDrawable); + } else { + this.setBackgroundDrawable(backgroundDrawable); + } + LogUtils.d(TAG, "背景图片加载成功"); + } + + /** + * 带压缩的 Bitmap 解码(避免大图导致 OOM) + * @param file 图片文件 + * @param maxWidth 最大宽度限制 + * @param maxHeight 最大高度限制 + * @return 压缩后的 Bitmap(null 表示失败) + */ + private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + 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; // 最小缩放比例为 1 + } + + // 正式加载图片并压缩 + options.inJustDecodeBounds = false; + options.inSampleSize = inSampleSize; + options.inPreferredConfig = Bitmap.Config.RGB_565; // 降低像素格式,减少内存占用 + return BitmapFactory.decodeFile(file.getAbsolutePath(), options); + } catch (Exception e) { + LogUtils.e(TAG, "图片压缩加载失败:" + e.getMessage()); + return null; + } + } + + /** + * 设置默认背景(图片加载失败时兜底) + */ + private void setDefaultBackground() { + // 可替换为项目自定义的默认背景图 + this.setBackgroundResource(R.drawable.default_background); + LogUtils.d(TAG, "已设置默认背景"); + } } diff --git a/powerbell/src/main/res/drawable/default_background.xml b/powerbell/src/main/res/drawable/default_background.xml new file mode 100644 index 00000000..f021b2ef --- /dev/null +++ b/powerbell/src/main/res/drawable/default_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/powerbell/src/main/res/layout/activity_mainunittest.xml b/powerbell/src/main/res/layout/activity_mainunittest.xml index f9adbc36..661c79a7 100644 --- a/powerbell/src/main/res/layout/activity_mainunittest.xml +++ b/powerbell/src/main/res/layout/activity_mainunittest.xml @@ -7,9 +7,10 @@ android:layout_height="match_parent"> + android:layout_width="match_parent" + android:layout_height="0dp" + android:id="@+id/activitymainunittestFrameLayout1" + android:layout_weight="1.0"/> diff --git a/powerbell/src/main/res/layout/dialog_networkbackground.xml b/powerbell/src/main/res/layout/dialog_networkbackground.xml index 76469d35..6f0bed2c 100644 --- a/powerbell/src/main/res/layout/dialog_networkbackground.xml +++ b/powerbell/src/main/res/layout/dialog_networkbackground.xml @@ -46,11 +46,10 @@ android:layout_marginTop="12dp" android:layout_gravity="center_vertical"> - + diff --git a/powerbell/src/main/res/layout/fragment_test_backgroundview.xml b/powerbell/src/main/res/layout/fragment_test_backgroundview.xml index 901c95a9..bef9a6c9 100644 --- a/powerbell/src/main/res/layout/fragment_test_backgroundview.xml +++ b/powerbell/src/main/res/layout/fragment_test_backgroundview.xml @@ -1,12 +1,16 @@ - + android:background="#FF7381FF"> + - + diff --git a/powerbell/src/main/res/layout/view_background.xml b/powerbell/src/main/res/layout/view_background.xml deleted file mode 100644 index b8dcccc2..00000000 --- a/powerbell/src/main/res/layout/view_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - -