两个类源码整合完成
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 03 22:24:41 GMT 2025
|
||||
#Wed Dec 03 22:37:16 GMT 2025
|
||||
stageCount=13
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.12
|
||||
buildCount=188
|
||||
buildCount=191
|
||||
baseBetaVersion=15.11.13
|
||||
|
||||
@@ -60,6 +60,6 @@ public class BackgroundViewTestFragment extends Fragment {
|
||||
|
||||
|
||||
void loadBackground() {
|
||||
//mBackgroundView.loadImage("/storage/emulated/0/Pictures/Gallery/owner/素材/1626915857361.png");
|
||||
mBackgroundView.loadImage("/storage/emulated/0/Pictures/Gallery/owner/素材/1626915857361.png");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,195 +3,252 @@ package cc.winboll.studio.powerbell.views;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
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 java.io.File;
|
||||
|
||||
/**
|
||||
* 图片背景设置视图(完全独立类,不依赖BackgroundSettingsActivity)
|
||||
* 仅通过文件路径自主解析比例,不接收任何外部比例参数
|
||||
* 基于Java7的BackgroundView(LinearLayout+ImageView,保持原图比例居中平铺)
|
||||
* 核心:ImageView保持原图比例,在LinearLayout中居中平铺,无拉伸、无裁剪
|
||||
*/
|
||||
public class BackgroundView extends View {
|
||||
private static final String TAG = "BackgroundView";
|
||||
private Bitmap mBackgroundBitmap; // 背景图片Bitmap
|
||||
private Paint mPaint; // 绘制画笔
|
||||
private RectF mDrawRect; // 绘制区域
|
||||
private Matrix mMatrix; // 缩放矩阵
|
||||
public class BackgroundView extends RelativeLayout {
|
||||
|
||||
public static final String TAG = "BackgroundView";
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mLlContainer; // 主容器LinearLayout
|
||||
private ImageView mIvBackground; // 图片显示控件
|
||||
private float mImageAspectRatio = 1.0f; // 原图宽高比(宽/高)
|
||||
|
||||
// ====================================== 构造器(Java7兼容) ======================================
|
||||
public BackgroundView(Context context) {
|
||||
super(context);
|
||||
LogUtils.d(TAG, "=== BackgroundView 构造器1 启动 ===");
|
||||
this.mContext = context;
|
||||
initView();
|
||||
}
|
||||
|
||||
public BackgroundView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LogUtils.d(TAG, "=== BackgroundView 构造器2 启动 ===");
|
||||
this.mContext = context;
|
||||
initView();
|
||||
}
|
||||
|
||||
public BackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
LogUtils.d(TAG, "=== BackgroundView 构造器3 启动 ===");
|
||||
this.mContext = context;
|
||||
initView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图(Java7语法,无Lambda)
|
||||
*/
|
||||
// ====================================== 初始化 ======================================
|
||||
private void initView() {
|
||||
Log.d(TAG, "initView: 初始化BackgroundView");
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true); // 抗锯齿
|
||||
mPaint.setFilterBitmap(true); // 过滤 bitmap,使绘制更清晰
|
||||
mDrawRect = new RectF();
|
||||
mMatrix = new Matrix();
|
||||
LogUtils.d(TAG, "=== initView 启动 ===");
|
||||
// 1. 配置当前控件:全屏+透明
|
||||
setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
setBackgroundColor(0x00000000);
|
||||
setBackground(new ColorDrawable(0x00000000));
|
||||
|
||||
// 2. 初始化主容器LinearLayout
|
||||
initLinearLayout();
|
||||
|
||||
// 3. 初始化ImageView
|
||||
initImageView();
|
||||
|
||||
// 4. 初始设置透明背景
|
||||
setDefaultTransparentBackground();
|
||||
LogUtils.d(TAG, "=== initView 完成 ===");
|
||||
}
|
||||
|
||||
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 完成 ===");
|
||||
}
|
||||
|
||||
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 完成 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载背景图片(仅接收BackgroundBean,仅读取文件路径,自主解析比例)
|
||||
* @param bean 背景图片信息(含文件路径,无比例信息)
|
||||
*/
|
||||
public void loadBackgroundBean(BackgroundBean bean) {
|
||||
Log.d(TAG, "loadBackgroundBean: 开始加载背景图片,文件路径:" + bean.getBackgroundScaledCompressFilePath());
|
||||
// 回收旧Bitmap,避免内存泄漏(Java7 手动回收)
|
||||
if (mBackgroundBitmap != null && !mBackgroundBitmap.isRecycled()) {
|
||||
mBackgroundBitmap.recycle();
|
||||
mBackgroundBitmap = null;
|
||||
Log.d(TAG, "loadBackgroundBean: 回收旧背景Bitmap");
|
||||
if(!bean.isUseBackgroundFile()) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取文件路径,自主解析图片(完全独立,不依赖外部比例)
|
||||
String imagePath = bean.getBackgroundScaledCompressFilePath();
|
||||
if (imagePath != null && !imagePath.isEmpty()) {
|
||||
File imageFile = new File(imagePath);
|
||||
mBackgroundBitmap = decodeBitmapFromFile(imageFile);
|
||||
Log.d(TAG, "loadBackgroundBean: 背景图片加载完成,是否成功:" + (mBackgroundBitmap != null));
|
||||
if(bean.isUseBackgroundScaledCompressFile()) {
|
||||
loadImage(bean.getBackgroundScaledCompressFilePath());
|
||||
} else {
|
||||
Log.e(TAG, "loadBackgroundBean: 图片路径为空,加载失败");
|
||||
}
|
||||
|
||||
// 刷新视图
|
||||
invalidate();
|
||||
|
||||
loadImage(bean.getBackgroundFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================== 对外方法 ======================================
|
||||
/**
|
||||
* 解析图片文件(Java7语法,采样率加载,自主计算比例)
|
||||
* @param imageFile 图片文件(来自BackgroundBean的路径)
|
||||
* @return 解析后的Bitmap(失败返回null)
|
||||
* 加载图片(保持原图比例,在LinearLayout中居中平铺)
|
||||
* @param imagePath 图片绝对路径
|
||||
*/
|
||||
private Bitmap decodeBitmapFromFile(File imageFile) {
|
||||
Log.d(TAG, "decodeBitmapFromFile: 解析图片文件,路径:" + imageFile.getAbsolutePath());
|
||||
// 校验文件有效性
|
||||
if (imageFile == null || !imageFile.exists() || !imageFile.isFile() || imageFile.length() <= 100) {
|
||||
Log.e(TAG, "decodeBitmapFromFile: 图片文件无效(不存在/非文件/过小)");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Java7 语法:Bitmap采样率加载(避免OOM)
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
// 第一步:仅获取图片尺寸,不加载完整Bitmap
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
|
||||
|
||||
// 第二步:计算采样率(最大尺寸限制为2048px,适配所有机型)
|
||||
int maxSize = 2048;
|
||||
int sampleSize = 1;
|
||||
// Java7 普通while循环,不使用增强for/ Lambda
|
||||
while (options.outWidth / sampleSize > maxSize || options.outHeight / sampleSize > maxSize) {
|
||||
sampleSize *= 2;
|
||||
}
|
||||
sampleSize = Math.min(sampleSize, 16); // 限制最大采样率,避免模糊
|
||||
Log.d(TAG, "decodeBitmapFromFile: 图片原始尺寸:" + options.outWidth + "x" + options.outHeight + ",采样率:" + sampleSize);
|
||||
|
||||
// 第三步:设置采样率,加载完整Bitmap
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565; // 节省内存(Java7 兼容配置)
|
||||
options.inPurgeable = true; // 允许内存不足时回收
|
||||
options.inInputShareable = true; // 配合inPurgeable使用
|
||||
|
||||
// 第四步:解析文件(捕获异常,Java7 手动捕获)
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
|
||||
if (bitmap == null || bitmap.isRecycled()) {
|
||||
Log.e(TAG, "decodeBitmapFromFile: 解析失败,Bitmap为空或已回收");
|
||||
return null;
|
||||
}
|
||||
Log.d(TAG, "decodeBitmapFromFile: 解析成功,Bitmap尺寸:" + bitmap.getWidth() + "x" + bitmap.getHeight() + ",内存大小:" + bitmap.getByteCount() / 1024 + "KB");
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e(TAG, "decodeBitmapFromFile: OOM异常,图片过大:" + e.getMessage());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "decodeBitmapFromFile: 解析异常:" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
// 获取视图实际尺寸
|
||||
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
Log.d(TAG, "onMeasure: 视图尺寸:" + viewWidth + "x" + viewHeight);
|
||||
mDrawRect.set(0, 0, viewWidth, viewHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Log.d(TAG, "onDraw: 绘制背景图片");
|
||||
// 若Bitmap为空,不绘制
|
||||
if (mBackgroundBitmap == null || mBackgroundBitmap.isRecycled()) {
|
||||
Log.w(TAG, "onDraw: 背景Bitmap为空,跳过绘制");
|
||||
public void loadImage(String imagePath) {
|
||||
LogUtils.d(TAG, "=== loadImage 启动,路径:" + imagePath + " ===");
|
||||
if (TextUtils.isEmpty(imagePath)) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算缩放比例(自主计算,基于视图尺寸和图片尺寸,无外部依赖)
|
||||
float viewWidth = mDrawRect.width();
|
||||
float viewHeight = mDrawRect.height();
|
||||
float bitmapWidth = mBackgroundBitmap.getWidth();
|
||||
float bitmapHeight = mBackgroundBitmap.getHeight();
|
||||
File imageFile = new File(imagePath);
|
||||
if (!imageFile.exists() || !imageFile.isFile()) {
|
||||
LogUtils.e(TAG, "图片文件无效");
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算缩放比例(保持比例,填满视图,裁剪多余部分)
|
||||
float scaleX = viewWidth / bitmapWidth;
|
||||
float scaleY = viewHeight / bitmapHeight;
|
||||
float scale = Math.max(scaleX, scaleY); // 取较大缩放比,确保填满视图
|
||||
// 计算原图比例
|
||||
if (!calculateImageAspectRatio(imageFile)) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算偏移量(水平/垂直居中)
|
||||
float translateX = (viewWidth - bitmapWidth * scale) / 2;
|
||||
float translateY = (viewHeight - bitmapHeight * scale) / 2;
|
||||
// 压缩加载Bitmap
|
||||
Bitmap bitmap = decodeBitmapWithCompress(imageFile, 1080, 1920);
|
||||
if (bitmap == null) {
|
||||
setDefaultTransparentBackground();
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置矩阵(Java7 手动设置,不使用Lambda)
|
||||
mMatrix.reset();
|
||||
mMatrix.postScale(scale, scale);
|
||||
mMatrix.postTranslate(translateX, translateY);
|
||||
// 设置图片
|
||||
mIvBackground.setImageDrawable(new BitmapDrawable(mContext.getResources(), bitmap));
|
||||
adjustImageViewSize(); // 调整尺寸
|
||||
LogUtils.d(TAG, "=== loadImage 完成 ===");
|
||||
}
|
||||
|
||||
// 绘制Bitmap
|
||||
canvas.drawBitmap(mBackgroundBitmap, mMatrix, mPaint);
|
||||
Log.d(TAG, "onDraw: 背景图片绘制完成,缩放比:" + scale + ",偏移量:(" + translateX + "," + translateY + ")");
|
||||
// ====================================== 内部工具方法 ======================================
|
||||
private boolean calculateImageAspectRatio(File file) {
|
||||
try {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
|
||||
|
||||
int width = options.outWidth;
|
||||
int height = options.outHeight;
|
||||
if (width <= 0 || height <= 0) {
|
||||
LogUtils.e(TAG, "图片尺寸无效");
|
||||
return false;
|
||||
}
|
||||
|
||||
mImageAspectRatio = (float) width / height;
|
||||
LogUtils.d(TAG, "原图比例:" + mImageAspectRatio);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "计算比例失败:" + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回收资源(Java7 手动回收,避免内存泄漏)
|
||||
* 调整ImageView尺寸(保持原图比例,在LinearLayout中居中平铺)
|
||||
*/
|
||||
public void releaseResource() {
|
||||
Log.d(TAG, "releaseResource: 回收BackgroundView资源");
|
||||
if (mBackgroundBitmap != null && !mBackgroundBitmap.isRecycled()) {
|
||||
mBackgroundBitmap.recycle();
|
||||
mBackgroundBitmap = null;
|
||||
private void adjustImageViewSize() {
|
||||
LogUtils.d(TAG, "=== adjustImageViewSize 启动 ===");
|
||||
if (mLlContainer == null || mIvBackground == null) {
|
||||
LogUtils.e(TAG, "控件为空");
|
||||
return;
|
||||
}
|
||||
mPaint = null;
|
||||
mDrawRect = null;
|
||||
mMatrix = null;
|
||||
|
||||
// 获取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;
|
||||
}
|
||||
|
||||
// ====================================== 重写方法 ======================================
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
adjustImageViewSize(); // 尺寸变化时重新调整
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user