diff --git a/powerbell/build.properties b/powerbell/build.properties index aa501695..a74a359f 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Wed Dec 03 18:54:44 GMT 2025 +#Wed Dec 03 21:14:37 GMT 2025 stageCount=13 libraryProject= baseVersion=15.11 publishVersion=15.11.12 -buildCount=178 +buildCount=180 baseBetaVersion=15.11.13 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java index 3d8e4eba..2f8eae2b 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/BackgroundSettingsActivity.java @@ -794,7 +794,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg previewBean.setIsUseBackgroundFile(true); previewBean.setIsUseBackgroundScaledCompressFile(true); mBgSourceUtils.saveSettings(); - mBackgroundView.loadBackground(previewBean); + mBackgroundView.loadBackgroundBean(previewBean); LogUtils.d(TAG, "isCropSuccess == true && mBackgroundView.loadBackground"); } else { // 其他失败场景 @@ -889,7 +889,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg // 1. 立即刷新(第一重:优先加载最新图片) if (mBackgroundView != null && !isFinishing()) { - mBackgroundView.loadBackground(mBgSourceUtils.getPreviewBackgroundBean()); + mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean()); LogUtils.d(TAG, "【双重刷新】第一重刷新完成(立即执行)"); } else { LogUtils.w(TAG, "【双重刷新】第一重刷新跳过:预览控件为空或页面即将销毁"); @@ -902,7 +902,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg public void run() { // 校验:页面未销毁+控件有效,才执行二次刷新 if (mBackgroundView != null && !isFinishing()) { - mBackgroundView.loadBackground(mBgSourceUtils.getPreviewBackgroundBean()); + mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean()); LogUtils.d(TAG, "【双重刷新】第二重刷新完成(延迟50ms执行)"); } else { LogUtils.w(TAG, "【双重刷新】第二重刷新跳过:页面已销毁或控件无效"); 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 84e664a3..1627d0bd 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 @@ -207,7 +207,7 @@ public class NetworkBackgroundDialog extends AlertDialog { mPreviewFilePath = previewFilePath; BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext); utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl); - mBackgroundView.loadBackground(utils.getPreviewBackgroundBean()); + mBackgroundView.loadBackgroundBean(utils.getPreviewBackgroundBean()); } catch (Exception e) { e.printStackTrace(); mBackgroundView.setBackgroundResource(R.drawable.ic_launcher); diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/fragments/MainViewFragment.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/fragments/MainViewFragment.java index 903bfdb0..eb671e4c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/fragments/MainViewFragment.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/fragments/MainViewFragment.java @@ -150,7 +150,7 @@ public class MainViewFragment extends Fragment { void loadBackground() { BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean(); - mBackgroundView.loadBackground(bean); + mBackgroundView.loadBackgroundBean(bean); } @Override @@ -324,7 +324,7 @@ public class MainViewFragment extends Fragment { BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity()); mBgSourceUtils.loadSettings(); BackgroundBean bean = utils.getCurrentBackgroundBean(); - mBackgroundView.loadBackground(bean); + mBackgroundView.loadBackgroundBean(bean); } Handler mHandler = new Handler(){ 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 6f7eead4..7504073b 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 @@ -60,8 +60,6 @@ public class BackgroundViewTestFragment extends Fragment { void loadBackground() { - mBgSourceUtils.loadSettings(); - BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean(); - mBackgroundView.loadBackground(bean); + mBackgroundView.loadImage("/storage/emulated/0/Pictures/Gallery/owner/素材/1626915857361.png"); } } 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 581fc641..214b5e66 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 @@ -5,352 +5,250 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.AttributeSet; import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; import android.widget.RelativeLayout; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.model.BackgroundBean; -import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils; import java.io.File; -import android.widget.ImageView.ScaleType; -import android.text.TextUtils; -import androidx.annotation.Nullable; -import cc.winboll.studio.powerbell.R; -import android.widget.LinearLayout; /** - * @Author ZhanGSKen&豆包大模型 - * @Date 2025/11/19 18:01 - * @Describe 背景图片视图控件(全透明背景 + 保持比例扩展 + 完全填充父视图 + 预览/正式模式切换) - * 核心功能:统一处理背景图片的加载、比例适配、模式切换,适配多布局场景和低版本Android系统 + * 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺) + * 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪 */ public class BackgroundView extends RelativeLayout { - public static final String TAG = "BackgroundView"; // 日志标记 + public static final String TAG = "BackgroundView"; - // 上下文对象(全局复用) private Context mContext; - private RelativeLayout mrlMain; - // 背景图片显示控件(核心子View) - private ImageView mivBackground; + private LinearLayout mLlContainer; // 主容器LinearLayout + private ImageView mIvBackground; // 图片显示控件 + private float mImageAspectRatio = 1.0f; // 原图宽高比(宽/高) - // 图片原始宽高比(控制图片不拉伸的核心参数,宽/高) - private float mnImageAspectRatio = 1.0f; - - // ====================================== 构造器(兼容所有布局场景)====================================== - /** - * 构造器1:代码创建控件时调用 - * @param context 上下文 - */ + // ====================================== 构造器(Java7兼容) ====================================== public BackgroundView(Context context) { super(context); - LogUtils.d(TAG, "=== BackgroundView 构造器1(代码创建)启动 ==="); + LogUtils.d(TAG, "=== BackgroundView 构造器1 启动 ==="); this.mContext = context; initView(); } - /** - * 构造器2:XML布局中使用时调用(无自定义属性) - * @param context 上下文 - * @param attrs 属性集 - */ public BackgroundView(Context context, AttributeSet attrs) { super(context, attrs); - LogUtils.d(TAG, "=== BackgroundView 构造器2(XML无属性)启动 ==="); + LogUtils.d(TAG, "=== BackgroundView 构造器2 启动 ==="); this.mContext = context; initView(); } - /** - * 构造器3:XML布局中使用时调用(带自定义属性+默认样式) - * @param context 上下文 - * @param attrs 属性集 - * @param defStyleAttr 默认样式属性 - */ public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - LogUtils.d(TAG, "=== BackgroundView 构造器3(XML带属性+默认样式)启动 ==="); + LogUtils.d(TAG, "=== BackgroundView 构造器3 启动 ==="); this.mContext = context; initView(); } - /** - * 构造器4:XML布局中使用时调用(带自定义属性+默认样式+自定义样式资源) - * @param context 上下文 - * @param attrs 属性集 - * @param defStyleAttr 默认样式属性 - * @param defStyleRes 自定义样式资源 - */ - public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - LogUtils.d(TAG, "=== BackgroundView 构造器4(XML全参数)启动 ==="); - this.mContext = context; - initView(); - } - - // ====================================== 初始化相关方法 ====================================== - /** - * 初始化视图(控件本身配置 + 子View初始化 + 初始背景加载) - * 所有构造器统一调用此方法,避免重复代码 - */ + // ====================================== 初始化 ====================================== private void initView() { - LogUtils.d(TAG, "=== initView(视图初始化)启动 ==="); - - // 1. 配置当前控件:完全填充父视图 + 全透明背景 + 无内边距 + LogUtils.d(TAG, "=== initView 启动 ==="); + // 1. 配置当前控件:全屏+透明 setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - setPadding(0, 0, 0, 0); // 取消自身内边距,避免父容器与控件间出现缝隙 - setBackgroundColor(0x00000000); // 全透明背景(ARGB:透明通道+黑色,无视觉影响) - setBackground(new ColorDrawable(0x00000000)); // 双重保障:兼容Android低版本,确保背景透明 + setBackgroundColor(0x00000000); + setBackground(new ColorDrawable(0x00000000)); - initBackgroundImageView(); + // 2. 初始化主容器LinearLayout + initLinearLayout(); - // 4. 初始加载:默认加载正式模式背景 + // 3. 初始化ImageView + initImageView(); + + // 4. 初始设置透明背景 setDefaultTransparentBackground(); - LogUtils.d(TAG, "=== initView(视图初始化)完成 ==="); + LogUtils.d(TAG, "=== initView 完成 ==="); } - /** - * 初始化内部ImageView(配置尺寸、缩放模式、背景等基础属性) - * 单独抽离方法,提高代码可读性 - */ - private void initBackgroundImageView() { - LogUtils.d(TAG, "=== initBackgroundImageView(内部ImageView初始化)启动 ==="); - mrlMain = (RelativeLayout)inflate(mContext, R.layout.view_background, null); - mrlMain.setBackgroundColor(0x00000000); // 全透明背景(ARGB:透明通道+黑色,无视觉影响) - mrlMain.setBackground(new ColorDrawable(0x00000000)); // 双重保障:兼容Android低版本,确保背景透明 - mivBackground = mrlMain.findViewById(R.id.bg_imageview); - // 将ImageView添加到当前控件(父容器) - this.addView(mrlMain); - LogUtils.d(TAG, "=== initBackgroundImageView(内部ImageView初始化)完成 ==="); + private void initLinearLayout() { + LogUtils.d(TAG, "=== initLinearLayout 启动 ==="); + mLlContainer = new LinearLayout(mContext); + // 配置LinearLayout:全屏+垂直方向+居中 + LinearLayout.LayoutParams llParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + ); + mLlContainer.setLayoutParams(llParams); + mLlContainer.setOrientation(LinearLayout.VERTICAL); + mLlContainer.setGravity(android.view.Gravity.CENTER); // 子View居中 + mLlContainer.setBackgroundColor(0x00000000); + this.addView(mLlContainer); + LogUtils.d(TAG, "=== initLinearLayout 完成 ==="); } - /** - * 【对外提供】重新加载预览模式背景图片 - * 逻辑:优先加载压缩图(省内存)→ 压缩图无效则加载原图 → 均无效则显示默认透明背景 - */ - public void loadBackground(BackgroundBean backgroundBean) { - LogUtils.d(TAG, "=== loadBackground(加载预览背景)启动 ==="); - // 校验预览Bean是否为空,为空则直接显示默认背景 - if (backgroundBean == null || !backgroundBean.isUseBackgroundFile()) { - LogUtils.e(TAG, "【loadBackground】预览Bean为空,正在加载默认图片"); - setDefaultTransparentBackground(); - return; - } - - // 加载背景图片路径 - String szBackgroundPath = backgroundBean.isUseBackgroundScaledCompressFile() ? backgroundBean.getBackgroundScaledCompressFilePath() : backgroundBean.getBackgroundFilePath(); - loadAndSetImageViewBackground(szBackgroundPath); + private void initImageView() { + LogUtils.d(TAG, "=== initImageView 启动 ==="); + mIvBackground = new ImageView(mContext); + // 配置ImageView:wrap_content+居中+透明背景 + LinearLayout.LayoutParams ivParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ); + mIvBackground.setLayoutParams(ivParams); + mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 保持比例+居中平铺 + mIvBackground.setBackgroundColor(0x00000000); + mLlContainer.addView(mIvBackground); + LogUtils.d(TAG, "=== initImageView 完成 ==="); } - - /** - * 核心逻辑:加载图片并设置到ImageView(正式/预览模式通用,统一处理逻辑) - * 负责:路径校验→文件校验→宽高比计算→压缩加载→尺寸调整→显示图片 - * @param imagePath 图片绝对路径(可为空) - */ - private void loadAndSetImageViewBackground(String imagePath) { - LogUtils.d(TAG, "=== loadAndSetImageViewBackground(加载并设置图片)启动 ==="); - LogUtils.d(TAG, String.format("imagePath %s", imagePath)); - - // 1. 路径校验:路径为空/空字符串,直接显示默认透明背景 - if (imagePath == null || imagePath.isEmpty()) { - LogUtils.e(TAG, "【loadAndSetImageViewBackground】加载失败:图片路径为空"); - setDefaultTransparentBackground(); - return; - } - - // 2. 文件校验:文件不存在/不是文件,直接显示默认透明背景 - File backgroundFile = new File(imagePath); - if (!backgroundFile.exists() || !backgroundFile.isFile()) { - LogUtils.e(TAG, "【loadAndSetImageViewBackground】加载失败:文件不存在或无效,路径:" + imagePath); - setDefaultTransparentBackground(); - return; - } - - // 3. 计算图片原始宽高比:计算失败则显示默认透明背景 - if (!calculateImageAspectRatio(backgroundFile)) { - LogUtils.e(TAG, "【loadAndSetImageViewBackground】加载失败:图片宽高比计算失败,路径:" + imagePath); - setDefaultTransparentBackground(); - return; - } - - // 4. 压缩加载Bitmap:避免OOM,加载失败则显示默认透明背景 - Bitmap bitmap = decodeBitmapWithCompress(backgroundFile, 1080, 1920); - if (bitmap == null) { - LogUtils.e(TAG, "【loadAndSetImageViewBackground】加载失败:图片压缩加载失败,路径:" + imagePath); - setDefaultTransparentBackground(); + + public void loadBackgroundBean(BackgroundBean bean) { + if(!bean.isUseBackgroundFile()) { + setDefaultTransparentBackground(); return; + } + if(bean.isUseBackgroundScaledCompressFile()) { + loadImage(bean.getBackgroundScaledCompressFilePath()); + } else { + + loadImage(bean.getBackgroundFilePath()); + } + } + + // ====================================== 对外方法 ====================================== + /** + * 加载图片(保持原图比例,在LinearLayout中居中平铺) + * @param imagePath 图片绝对路径 + */ + public void loadImage(String imagePath) { + LogUtils.d(TAG, "=== loadImage 启动,路径:" + imagePath + " ==="); + if (TextUtils.isEmpty(imagePath)) { + setDefaultTransparentBackground(); + return; } - - // 5. 设置图片到ImageView(兼容低版本系统) - Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap); - mivBackground.setImageDrawable(backgroundDrawable); + File imageFile = new File(imagePath); + if (!imageFile.exists() || !imageFile.isFile()) { + LogUtils.e(TAG, "图片文件无效"); + setDefaultTransparentBackground(); + return; + } - // 6. 调整ImageView尺寸(按图片比例扩展到父控件,确保不拉伸) - adjustImageViewSize(); + // 计算原图比例 + if (!calculateImageAspectRatio(imageFile)) { + setDefaultTransparentBackground(); + return; + } - LogUtils.d(TAG, "【loadAndSetImageViewBackground】图片加载成功"); - LogUtils.d(TAG, "→ 图片路径:" + imagePath); - LogUtils.d(TAG, "→ 图片宽高比:" + mnImageAspectRatio); - LogUtils.d(TAG, "=== loadAndSetImageViewBackground(加载并设置图片)完成(加载成功)==="); + // 压缩加载Bitmap + Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920); + if (bitmap == null) { + setDefaultTransparentBackground(); + return; + } + + // 设置图片 + mIvBackground.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap)); + adjustImageViewSize(); // 调整尺寸 + LogUtils.d(TAG, "=== loadImage 完成 ==="); } - /** - * 计算图片原始宽高比(宽/高)→ 控制图片不拉伸的核心方法 - * 仅获取图片尺寸,不加载完整图片到内存(节省内存) - * @param file 图片文件(已校验非空) - * @return 计算成功:true,计算失败:false - */ + // ====================================== 内部工具方法 ====================================== private boolean calculateImageAspectRatio(File file) { - LogUtils.d(TAG, "=== calculateImageAspectRatio(计算图片宽高比)启动 ==="); 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, "【calculateImageAspectRatio】计算失败:图片尺寸无效,宽:" + imageWidth + ",高:" + imageHeight); - return false; + int width = options.outWidth; + int height = options.outHeight; + if (width <= 0 || height <= 0) { + LogUtils.e(TAG, "图片尺寸无效"); + return false; } - // 计算并保存图片原始宽高比(宽/高) - mnImageAspectRatio = (float) imageWidth / imageHeight; - LogUtils.d(TAG, "【calculateImageAspectRatio】计算成功"); + mImageAspectRatio = (float) width / height; + LogUtils.d(TAG, "原图比例:" + mImageAspectRatio); return true; } catch (Exception e) { - LogUtils.d(TAG, "=== calculateImageAspectRatio(计算图片宽高比)完成(发生异常)==="); + LogUtils.e(TAG, "计算比例失败:" + e.getMessage()); return false; } } - /** - * 调整ImageView尺寸(核心方法) - * 逻辑:按图片原始宽高比,计算ImageView最大尺寸(填满父控件且不拉伸),并保持居中 - * 适配场景:控件初始化、图片加载后、父容器尺寸变化(如屏幕旋转) - */ - private void adjustImageViewSize() { - // 2. 获取根容器bg_main尺寸(以bg_main为基准,实现居中平铺) - int parentWidth = mrlMain.getWidth(); - int parentHeight = mrlMain.getHeight(); - - // 3. bg_main未测量完成(宽/高为0),延迟调整(避免尺寸计算错误) - if (parentWidth == 0 || parentHeight == 0) { - postDelayed(new Runnable() { - @Override - public void run() { - adjustImageViewSize(); // 延迟后重新调整 - } - }, 10); // 10ms延迟,平衡响应速度与测量稳定性 - return; - } - - // 4. 初始化ImageView目标宽高(核心:严格保持原图宽高比,适配bg_main居中平铺) - int imageViewWidth, imageViewHeight; - float imageRatio = mnImageAspectRatio; // 原图宽高比(宽/高,确保比例不变) - float parentRatio = (float) parentWidth / parentHeight; // bg_main容器宽高比(宽/高) - - // 关键逻辑:按比例计算ImageView最大尺寸(完整显示图片+适配bg_main+无拉伸) - if (imageRatio >= parentRatio) { - // 情况1:原图比bg_main更宽(横图)→ 按bg_main宽度缩放,高度按原图比例适配(横向填满,纵向不超出) - imageViewWidth = parentWidth; // 宽度=bg_main宽度(横向平铺填满) - imageViewHeight = Math.round(imageViewWidth / imageRatio); // 高度按原图比例计算 - } else { - // 情况2:原图比bg_main更高(竖图)→ 按bg_main高度缩放,宽度按原图比例适配(纵向填满,横向不超出) - imageViewHeight = parentHeight; // 高度=bg_main高度(纵向平铺填满) - imageViewWidth = Math.round(imageViewHeight * imageRatio); // 宽度按原图比例计算 - } - - // 5. 应用尺寸到ImageView(匹配xml布局参数,确保居中) - // 适配xml中RelativeLayout的LayoutParams(与布局文件根容器一致) - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mivBackground.getLayoutParams(); - layoutParams.width = imageViewWidth; // 按比例计算的宽度(适配bg_main) - layoutParams.height = imageViewHeight; // 按比例计算的高度(适配bg_main) - layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); // 强制居中(匹配需求:图片在bg_main中居中) - mivBackground.setLayoutParams(layoutParams); - - // 6. 关键:设置缩放模式(匹配xml布局+保持比例+居中平铺) - mivBackground.setScaleType(ImageView.ScaleType.MATRIX); - // 确保ImageView背景透明(避免遮挡,适配布局视觉效果) - mivBackground.setBackgroundColor(0x00000000); - } - - - /** - * 带压缩的Bitmap解码(通用方法) - * 逻辑:仅缩小图片(不放大),保持原始比例,降低内存占用,避免OOM - * @param file 图片文件 - * @param maxWidth 最大宽度(1080px,适配主流手机屏幕) - * @param maxHeight 最大高度(1920px,适配主流手机屏幕) - * @return 压缩后的Bitmap(null表示解码失败) - */ private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) { - LogUtils.d(TAG, "=== decodeBitmapWithCompress(压缩解码Bitmap)启动 ==="); 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; // 压缩比例最小为1(不压缩) - } + int scaleX = options.outWidth / maxWidth; + int scaleY = options.outHeight / maxHeight; + int inSampleSize = Math.max(scaleX, scaleY); + if (inSampleSize <= 0) inSampleSize = 1; - // 正式解码图片(配置压缩参数+省内存参数) - options.inJustDecodeBounds = false; // 允许加载完整图片 - options.inSampleSize = inSampleSize; // 设置压缩比例 - options.inPreferredConfig = Bitmap.Config.RGB_565; // 内存优化:比ARGB_8888省一半内存 - Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); - - if (bitmap != null) { - LogUtils.d(TAG, "【decodeBitmapWithCompress】压缩解码成功"); - LogUtils.d(TAG, "→ 压缩比例:" + inSampleSize); - LogUtils.d(TAG, "→ 解码后Bitmap尺寸:" + bitmap.getWidth() + "x" + bitmap.getHeight()); - } else { - LogUtils.e(TAG, "【decodeBitmapWithCompress】压缩解码失败:Bitmap为空"); - } - - LogUtils.d(TAG, "=== decodeBitmapWithCompress(压缩解码Bitmap)完成 ==="); - return bitmap; + options.inJustDecodeBounds = false; + options.inSampleSize = inSampleSize; + options.inPreferredConfig = Bitmap.Config.RGB_565; + return BitmapFactory.decodeFile(file.getAbsolutePath(), options); } catch (Exception e) { - LogUtils.e(TAG, "【decodeBitmapWithCompress】压缩解码失败:" + e.getMessage(), e); - LogUtils.d(TAG, "=== decodeBitmapWithCompress(压缩解码Bitmap)完成(发生异常)==="); + LogUtils.e(TAG, "压缩解码失败:" + e.getMessage()); return null; } } /** - * 通用兜底:设置默认透明背景(图片加载失败/路径无效时使用) - * 确保:无有效图片时,控件完全透明,不遮挡下层视图 + * 调整ImageView尺寸(保持原图比例,在LinearLayout中居中平铺) */ - private void setDefaultTransparentBackground() { - LogUtils.d(TAG, "=== setDefaultTransparentBackground(设置通用默认透明背景)启动 ==="); - mivBackground.setBackground(new ColorDrawable(0x00000000)); // ImageView背景全透明 - mivBackground.setImageBitmap(null); // 清空ImageView的图片 - mnImageAspectRatio = 1.0f; // 重置宽高比(避免影响下次图片加载) + 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(); + } + }, 10); + return; + } + + // 计算ImageView尺寸(保持比例,不超出LinearLayout) + int ivWidth, ivHeight; + if (mImageAspectRatio >= 1.0f) { + ivWidth = Math.min((int) (llHeight * mImageAspectRatio), llWidth); + ivHeight = (int) (ivWidth / mImageAspectRatio); + } else { + ivHeight = Math.min((int) (llWidth / mImageAspectRatio), llHeight); + 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); // 确保居中平铺 + + LogUtils.d(TAG, "ImageView尺寸:" + ivWidth + "x" + ivHeight); + LogUtils.d(TAG, "=== adjustImageViewSize 完成 ==="); } - // ====================================== 重写父类方法 ====================================== - /** - * 重写父类方法:父容器尺寸变化时调用(如屏幕旋转、窗口大小调整) - * 作用:确保控件尺寸变化后,图片仍保持比例+填满父控件 - * @param w 新宽度 - * @param h 新高度 - * @param oldw 旧宽度 - * @param oldh 旧高度 - */ + private void setDefaultTransparentBackground() { + mIvBackground.setImageBitmap(null); + mIvBackground.setBackgroundColor(0x00000000); + mImageAspectRatio = 1.0f; + } + + // ====================================== 重写方法 ====================================== @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - adjustImageViewSize(); // 重新调整ImageView尺寸,适配新的控件尺寸 - } + adjustImageViewSize(); // 尺寸变化时重新调整 + } } -