20251204_051841_861
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#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
|
stageCount=13
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.11
|
baseVersion=15.11
|
||||||
publishVersion=15.11.12
|
publishVersion=15.11.12
|
||||||
buildCount=178
|
buildCount=180
|
||||||
baseBetaVersion=15.11.13
|
baseBetaVersion=15.11.13
|
||||||
|
|||||||
@@ -794,7 +794,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
previewBean.setIsUseBackgroundFile(true);
|
previewBean.setIsUseBackgroundFile(true);
|
||||||
previewBean.setIsUseBackgroundScaledCompressFile(true);
|
previewBean.setIsUseBackgroundScaledCompressFile(true);
|
||||||
mBgSourceUtils.saveSettings();
|
mBgSourceUtils.saveSettings();
|
||||||
mBackgroundView.loadBackground(previewBean);
|
mBackgroundView.loadBackgroundBean(previewBean);
|
||||||
LogUtils.d(TAG, "isCropSuccess == true && mBackgroundView.loadBackground");
|
LogUtils.d(TAG, "isCropSuccess == true && mBackgroundView.loadBackground");
|
||||||
} else {
|
} else {
|
||||||
// 其他失败场景
|
// 其他失败场景
|
||||||
@@ -889,7 +889,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
|
|
||||||
// 1. 立即刷新(第一重:优先加载最新图片)
|
// 1. 立即刷新(第一重:优先加载最新图片)
|
||||||
if (mBackgroundView != null && !isFinishing()) {
|
if (mBackgroundView != null && !isFinishing()) {
|
||||||
mBackgroundView.loadBackground(mBgSourceUtils.getPreviewBackgroundBean());
|
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||||
LogUtils.d(TAG, "【双重刷新】第一重刷新完成(立即执行)");
|
LogUtils.d(TAG, "【双重刷新】第一重刷新完成(立即执行)");
|
||||||
} else {
|
} else {
|
||||||
LogUtils.w(TAG, "【双重刷新】第一重刷新跳过:预览控件为空或页面即将销毁");
|
LogUtils.w(TAG, "【双重刷新】第一重刷新跳过:预览控件为空或页面即将销毁");
|
||||||
@@ -902,7 +902,7 @@ public class BackgroundSettingsActivity extends WinBoLLActivity implements Backg
|
|||||||
public void run() {
|
public void run() {
|
||||||
// 校验:页面未销毁+控件有效,才执行二次刷新
|
// 校验:页面未销毁+控件有效,才执行二次刷新
|
||||||
if (mBackgroundView != null && !isFinishing()) {
|
if (mBackgroundView != null && !isFinishing()) {
|
||||||
mBackgroundView.loadBackground(mBgSourceUtils.getPreviewBackgroundBean());
|
mBackgroundView.loadBackgroundBean(mBgSourceUtils.getPreviewBackgroundBean());
|
||||||
LogUtils.d(TAG, "【双重刷新】第二重刷新完成(延迟50ms执行)");
|
LogUtils.d(TAG, "【双重刷新】第二重刷新完成(延迟50ms执行)");
|
||||||
} else {
|
} else {
|
||||||
LogUtils.w(TAG, "【双重刷新】第二重刷新跳过:页面已销毁或控件无效");
|
LogUtils.w(TAG, "【双重刷新】第二重刷新跳过:页面已销毁或控件无效");
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public class NetworkBackgroundDialog extends AlertDialog {
|
|||||||
mPreviewFilePath = previewFilePath;
|
mPreviewFilePath = previewFilePath;
|
||||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
|
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(mContext);
|
||||||
utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl);
|
utils.saveFileToPreviewBean(new File(mPreviewFilePath), mPreviewFileUrl);
|
||||||
mBackgroundView.loadBackground(utils.getPreviewBackgroundBean());
|
mBackgroundView.loadBackgroundBean(utils.getPreviewBackgroundBean());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
mBackgroundView.setBackgroundResource(R.drawable.ic_launcher);
|
mBackgroundView.setBackgroundResource(R.drawable.ic_launcher);
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public class MainViewFragment extends Fragment {
|
|||||||
|
|
||||||
void loadBackground() {
|
void loadBackground() {
|
||||||
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
||||||
mBackgroundView.loadBackground(bean);
|
mBackgroundView.loadBackgroundBean(bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -324,7 +324,7 @@ public class MainViewFragment extends Fragment {
|
|||||||
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
|
BackgroundSourceUtils utils = BackgroundSourceUtils.getInstance(getActivity());
|
||||||
mBgSourceUtils.loadSettings();
|
mBgSourceUtils.loadSettings();
|
||||||
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
BackgroundBean bean = utils.getCurrentBackgroundBean();
|
||||||
mBackgroundView.loadBackground(bean);
|
mBackgroundView.loadBackgroundBean(bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler mHandler = new Handler(){
|
Handler mHandler = new Handler(){
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ public class BackgroundViewTestFragment extends Fragment {
|
|||||||
|
|
||||||
|
|
||||||
void loadBackground() {
|
void loadBackground() {
|
||||||
mBgSourceUtils.loadSettings();
|
mBackgroundView.loadImage("/storage/emulated/0/Pictures/Gallery/owner/素材/1626915857361.png");
|
||||||
BackgroundBean bean = mBgSourceUtils.getCurrentBackgroundBean();
|
|
||||||
mBackgroundView.loadBackground(bean);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,352 +5,250 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ImageView.ScaleType;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
import cc.winboll.studio.powerbell.model.BackgroundBean;
|
||||||
import cc.winboll.studio.powerbell.utils.BackgroundSourceUtils;
|
|
||||||
import java.io.File;
|
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&豆包大模型<zhangsken@qq.com>
|
* 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺)
|
||||||
* @Date 2025/11/19 18:01
|
* 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪
|
||||||
* @Describe 背景图片视图控件(全透明背景 + 保持比例扩展 + 完全填充父视图 + 预览/正式模式切换)
|
|
||||||
* 核心功能:统一处理背景图片的加载、比例适配、模式切换,适配多布局场景和低版本Android系统
|
|
||||||
*/
|
*/
|
||||||
public class BackgroundView extends RelativeLayout {
|
public class BackgroundView extends RelativeLayout {
|
||||||
|
|
||||||
public static final String TAG = "BackgroundView"; // 日志标记
|
public static final String TAG = "BackgroundView";
|
||||||
|
|
||||||
// 上下文对象(全局复用)
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private RelativeLayout mrlMain;
|
private LinearLayout mLlContainer; // 主容器LinearLayout
|
||||||
// 背景图片显示控件(核心子View)
|
private ImageView mIvBackground; // 图片显示控件
|
||||||
private ImageView mivBackground;
|
private float mImageAspectRatio = 1.0f; // 原图宽高比(宽/高)
|
||||||
|
|
||||||
// 图片原始宽高比(控制图片不拉伸的核心参数,宽/高)
|
// ====================================== 构造器(Java7兼容) ======================================
|
||||||
private float mnImageAspectRatio = 1.0f;
|
|
||||||
|
|
||||||
// ====================================== 构造器(兼容所有布局场景)======================================
|
|
||||||
/**
|
|
||||||
* 构造器1:代码创建控件时调用
|
|
||||||
* @param context 上下文
|
|
||||||
*/
|
|
||||||
public BackgroundView(Context context) {
|
public BackgroundView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
LogUtils.d(TAG, "=== BackgroundView 构造器1(代码创建)启动 ===");
|
LogUtils.d(TAG, "=== BackgroundView 构造器1 启动 ===");
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
initView();
|
initView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造器2:XML布局中使用时调用(无自定义属性)
|
|
||||||
* @param context 上下文
|
|
||||||
* @param attrs 属性集
|
|
||||||
*/
|
|
||||||
public BackgroundView(Context context, AttributeSet attrs) {
|
public BackgroundView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
LogUtils.d(TAG, "=== BackgroundView 构造器2(XML无属性)启动 ===");
|
LogUtils.d(TAG, "=== BackgroundView 构造器2 启动 ===");
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
initView();
|
initView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构造器3:XML布局中使用时调用(带自定义属性+默认样式)
|
|
||||||
* @param context 上下文
|
|
||||||
* @param attrs 属性集
|
|
||||||
* @param defStyleAttr 默认样式属性
|
|
||||||
*/
|
|
||||||
public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
|
public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
LogUtils.d(TAG, "=== BackgroundView 构造器3(XML带属性+默认样式)启动 ===");
|
LogUtils.d(TAG, "=== BackgroundView 构造器3 启动 ===");
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
initView();
|
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() {
|
private void initView() {
|
||||||
LogUtils.d(TAG, "=== initView(视图初始化)启动 ===");
|
LogUtils.d(TAG, "=== initView 启动 ===");
|
||||||
|
// 1. 配置当前控件:全屏+透明
|
||||||
// 1. 配置当前控件:完全填充父视图 + 全透明背景 + 无内边距
|
|
||||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||||
setPadding(0, 0, 0, 0); // 取消自身内边距,避免父容器与控件间出现缝隙
|
setBackgroundColor(0x00000000);
|
||||||
setBackgroundColor(0x00000000); // 全透明背景(ARGB:透明通道+黑色,无视觉影响)
|
setBackground(new ColorDrawable(0x00000000));
|
||||||
setBackground(new ColorDrawable(0x00000000)); // 双重保障:兼容Android低版本,确保背景透明
|
|
||||||
|
|
||||||
initBackgroundImageView();
|
// 2. 初始化主容器LinearLayout
|
||||||
|
initLinearLayout();
|
||||||
|
|
||||||
// 4. 初始加载:默认加载正式模式背景
|
// 3. 初始化ImageView
|
||||||
|
initImageView();
|
||||||
|
|
||||||
|
// 4. 初始设置透明背景
|
||||||
setDefaultTransparentBackground();
|
setDefaultTransparentBackground();
|
||||||
LogUtils.d(TAG, "=== initView(视图初始化)完成 ===");
|
LogUtils.d(TAG, "=== initView 完成 ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void initLinearLayout() {
|
||||||
* 初始化内部ImageView(配置尺寸、缩放模式、背景等基础属性)
|
LogUtils.d(TAG, "=== initLinearLayout 启动 ===");
|
||||||
* 单独抽离方法,提高代码可读性
|
mLlContainer = new LinearLayout(mContext);
|
||||||
*/
|
// 配置LinearLayout:全屏+垂直方向+居中
|
||||||
private void initBackgroundImageView() {
|
LinearLayout.LayoutParams llParams = new LinearLayout.LayoutParams(
|
||||||
LogUtils.d(TAG, "=== initBackgroundImageView(内部ImageView初始化)启动 ===");
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
mrlMain = (RelativeLayout)inflate(mContext, R.layout.view_background, null);
|
LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
mrlMain.setBackgroundColor(0x00000000); // 全透明背景(ARGB:透明通道+黑色,无视觉影响)
|
);
|
||||||
mrlMain.setBackground(new ColorDrawable(0x00000000)); // 双重保障:兼容Android低版本,确保背景透明
|
mLlContainer.setLayoutParams(llParams);
|
||||||
mivBackground = mrlMain.findViewById(R.id.bg_imageview);
|
mLlContainer.setOrientation(LinearLayout.VERTICAL);
|
||||||
// 将ImageView添加到当前控件(父容器)
|
mLlContainer.setGravity(android.view.Gravity.CENTER); // 子View居中
|
||||||
this.addView(mrlMain);
|
mLlContainer.setBackgroundColor(0x00000000);
|
||||||
LogUtils.d(TAG, "=== initBackgroundImageView(内部ImageView初始化)完成 ===");
|
this.addView(mLlContainer);
|
||||||
|
LogUtils.d(TAG, "=== initLinearLayout 完成 ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void initImageView() {
|
||||||
* 【对外提供】重新加载预览模式背景图片
|
LogUtils.d(TAG, "=== initImageView 启动 ===");
|
||||||
* 逻辑:优先加载压缩图(省内存)→ 压缩图无效则加载原图 → 均无效则显示默认透明背景
|
mIvBackground = new ImageView(mContext);
|
||||||
*/
|
// 配置ImageView:wrap_content+居中+透明背景
|
||||||
public void loadBackground(BackgroundBean backgroundBean) {
|
LinearLayout.LayoutParams ivParams = new LinearLayout.LayoutParams(
|
||||||
LogUtils.d(TAG, "=== loadBackground(加载预览背景)启动 ===");
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
// 校验预览Bean是否为空,为空则直接显示默认背景
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
if (backgroundBean == null || !backgroundBean.isUseBackgroundFile()) {
|
);
|
||||||
LogUtils.e(TAG, "【loadBackground】预览Bean为空,正在加载默认图片");
|
mIvBackground.setLayoutParams(ivParams);
|
||||||
setDefaultTransparentBackground();
|
mIvBackground.setScaleType(ScaleType.FIT_CENTER); // 保持比例+居中平铺
|
||||||
return;
|
mIvBackground.setBackgroundColor(0x00000000);
|
||||||
}
|
mLlContainer.addView(mIvBackground);
|
||||||
|
LogUtils.d(TAG, "=== initImageView 完成 ===");
|
||||||
// 加载背景图片路径
|
|
||||||
String szBackgroundPath = backgroundBean.isUseBackgroundScaledCompressFile() ? backgroundBean.getBackgroundScaledCompressFilePath() : backgroundBean.getBackgroundFilePath();
|
|
||||||
loadAndSetImageViewBackground(szBackgroundPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void loadBackgroundBean(BackgroundBean bean) {
|
||||||
* 核心逻辑:加载图片并设置到ImageView(正式/预览模式通用,统一处理逻辑)
|
if(!bean.isUseBackgroundFile()) {
|
||||||
* 负责:路径校验→文件校验→宽高比计算→压缩加载→尺寸调整→显示图片
|
setDefaultTransparentBackground();
|
||||||
* @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();
|
|
||||||
return;
|
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(兼容低版本系统)
|
File imageFile = new File(imagePath);
|
||||||
Drawable backgroundDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
|
if (!imageFile.exists() || !imageFile.isFile()) {
|
||||||
mivBackground.setImageDrawable(backgroundDrawable);
|
LogUtils.e(TAG, "图片文件无效");
|
||||||
|
setDefaultTransparentBackground();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 6. 调整ImageView尺寸(按图片比例扩展到父控件,确保不拉伸)
|
// 计算原图比例
|
||||||
adjustImageViewSize();
|
if (!calculateImageAspectRatio(imageFile)) {
|
||||||
|
setDefaultTransparentBackground();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LogUtils.d(TAG, "【loadAndSetImageViewBackground】图片加载成功");
|
// 压缩加载Bitmap
|
||||||
LogUtils.d(TAG, "→ 图片路径:" + imagePath);
|
Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920);
|
||||||
LogUtils.d(TAG, "→ 图片宽高比:" + mnImageAspectRatio);
|
if (bitmap == null) {
|
||||||
LogUtils.d(TAG, "=== loadAndSetImageViewBackground(加载并设置图片)完成(加载成功)===");
|
setDefaultTransparentBackground();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置图片
|
||||||
|
mIvBackground.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
|
||||||
|
adjustImageViewSize(); // 调整尺寸
|
||||||
|
LogUtils.d(TAG, "=== loadImage 完成 ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ====================================== 内部工具方法 ======================================
|
||||||
* 计算图片原始宽高比(宽/高)→ 控制图片不拉伸的核心方法
|
|
||||||
* 仅获取图片尺寸,不加载完整图片到内存(节省内存)
|
|
||||||
* @param file 图片文件(已校验非空)
|
|
||||||
* @return 计算成功:true,计算失败:false
|
|
||||||
*/
|
|
||||||
private boolean calculateImageAspectRatio(File file) {
|
private boolean calculateImageAspectRatio(File file) {
|
||||||
LogUtils.d(TAG, "=== calculateImageAspectRatio(计算图片宽高比)启动 ===");
|
|
||||||
try {
|
try {
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
options.inJustDecodeBounds = true; // 仅获取图片尺寸,不加载图片到内存(关键配置)
|
options.inJustDecodeBounds = true;
|
||||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||||
|
|
||||||
// 获取图片原始宽高
|
int width = options.outWidth;
|
||||||
int imageWidth = options.outWidth;
|
int height = options.outHeight;
|
||||||
int imageHeight = options.outHeight;
|
if (width <= 0 || height <= 0) {
|
||||||
|
LogUtils.e(TAG, "图片尺寸无效");
|
||||||
// 校验宽高有效性(宽/高必须大于0,否则视为无效图片)
|
return false;
|
||||||
if (imageWidth <= 0 || imageHeight <= 0) {
|
|
||||||
LogUtils.e(TAG, "【calculateImageAspectRatio】计算失败:图片尺寸无效,宽:" + imageWidth + ",高:" + imageHeight);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算并保存图片原始宽高比(宽/高)
|
mImageAspectRatio = (float) width / height;
|
||||||
mnImageAspectRatio = (float) imageWidth / imageHeight;
|
LogUtils.d(TAG, "原图比例:" + mImageAspectRatio);
|
||||||
LogUtils.d(TAG, "【calculateImageAspectRatio】计算成功");
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.d(TAG, "=== calculateImageAspectRatio(计算图片宽高比)完成(发生异常)===");
|
LogUtils.e(TAG, "计算比例失败:" + e.getMessage());
|
||||||
return false;
|
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) {
|
private Bitmap decodeBitmapWithCompress(File file, int maxWidth, int maxHeight) {
|
||||||
LogUtils.d(TAG, "=== decodeBitmapWithCompress(压缩解码Bitmap)启动 ===");
|
|
||||||
try {
|
try {
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
options.inJustDecodeBounds = true; // 先获取图片尺寸,不加载完整图片
|
options.inJustDecodeBounds = true;
|
||||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||||
|
|
||||||
// 计算压缩比例(仅缩小,不放大,取宽高压缩比例的最大值)
|
int scaleX = options.outWidth / maxWidth;
|
||||||
int scaleX = options.outWidth / maxWidth; // 宽度方向压缩比例
|
int scaleY = options.outHeight / maxHeight;
|
||||||
int scaleY = options.outHeight / maxHeight; // 高度方向压缩比例
|
int inSampleSize = Math.max(scaleX, scaleY);
|
||||||
int inSampleSize = Math.max(scaleX, scaleY); // 最终压缩比例(取最大值,确保不超过最大尺寸)
|
if (inSampleSize <= 0) inSampleSize = 1;
|
||||||
if (inSampleSize <= 0) {
|
|
||||||
inSampleSize = 1; // 压缩比例最小为1(不压缩)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 正式解码图片(配置压缩参数+省内存参数)
|
options.inJustDecodeBounds = false;
|
||||||
options.inJustDecodeBounds = false; // 允许加载完整图片
|
options.inSampleSize = inSampleSize;
|
||||||
options.inSampleSize = inSampleSize; // 设置压缩比例
|
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 内存优化:比ARGB_8888省一半内存
|
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||||
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;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.e(TAG, "【decodeBitmapWithCompress】压缩解码失败:" + e.getMessage(), e);
|
LogUtils.e(TAG, "压缩解码失败:" + e.getMessage());
|
||||||
LogUtils.d(TAG, "=== decodeBitmapWithCompress(压缩解码Bitmap)完成(发生异常)===");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用兜底:设置默认透明背景(图片加载失败/路径无效时使用)
|
* 调整ImageView尺寸(保持原图比例,在LinearLayout中居中平铺)
|
||||||
* 确保:无有效图片时,控件完全透明,不遮挡下层视图
|
|
||||||
*/
|
*/
|
||||||
private void setDefaultTransparentBackground() {
|
private void adjustImageViewSize() {
|
||||||
LogUtils.d(TAG, "=== setDefaultTransparentBackground(设置通用默认透明背景)启动 ===");
|
LogUtils.d(TAG, "=== adjustImageViewSize 启动 ===");
|
||||||
mivBackground.setBackground(new ColorDrawable(0x00000000)); // ImageView背景全透明
|
if (mLlContainer == null || mIvBackground == null) {
|
||||||
mivBackground.setImageBitmap(null); // 清空ImageView的图片
|
LogUtils.e(TAG, "控件为空");
|
||||||
mnImageAspectRatio = 1.0f; // 重置宽高比(避免影响下次图片加载)
|
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 完成 ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================================== 重写父类方法 ======================================
|
private void setDefaultTransparentBackground() {
|
||||||
/**
|
mIvBackground.setImageBitmap(null);
|
||||||
* 重写父类方法:父容器尺寸变化时调用(如屏幕旋转、窗口大小调整)
|
mIvBackground.setBackgroundColor(0x00000000);
|
||||||
* 作用:确保控件尺寸变化后,图片仍保持比例+填满父控件
|
mImageAspectRatio = 1.0f;
|
||||||
* @param w 新宽度
|
}
|
||||||
* @param h 新高度
|
|
||||||
* @param oldw 旧宽度
|
// ====================================== 重写方法 ======================================
|
||||||
* @param oldh 旧高度
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
adjustImageViewSize(); // 重新调整ImageView尺寸,适配新的控件尺寸
|
adjustImageViewSize(); // 尺寸变化时重新调整
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user