From b6a820b281720216137b54a0857fb7004fb506f3 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Sat, 13 Dec 2025 01:25:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9B=BE=E5=8A=9B=E7=83=AD?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E8=A7=86=E5=9B=BE=E3=80=82=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E7=9B=BE=E5=80=BC=E6=9B=B4=E6=96=B0=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contacts/build.properties | 4 +- .../winboll/studio/contacts/MainActivity.java | 26 ++- .../cc/winboll/studio/contacts/dun/Rules.java | 5 +- .../contacts/views/DunTemperatureView.java | 179 ++++++++++++------ .../src/main/res/layout/activity_main.xml | 17 +- 5 files changed, 161 insertions(+), 70 deletions(-) diff --git a/contacts/build.properties b/contacts/build.properties index 57b9903..7fb6b16 100644 --- a/contacts/build.properties +++ b/contacts/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Fri Dec 12 13:17:41 GMT 2025 +#Fri Dec 12 17:21:18 GMT 2025 stageCount=1 libraryProject= baseVersion=15.12 publishVersion=15.12.0 -buildCount=61 +buildCount=75 baseBetaVersion=15.12.1 diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java index 87e07b5..7cb8810 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/MainActivity.java @@ -344,10 +344,10 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv initPhoneStateListener(); LogUtils.d(TAG, "initUIAndLogic: 电话状态监听器初始化完成"); - // 6. 盾值视图初始化(Java7分步写法,禁止链式调用) - DunTemperatureView tempView = (DunTemperatureView) findViewById(R.id.dun_temp_view); - tempView.setMaxValue(Rules.getInstance(this).getSettingsModel().getDunTotalCount()); - tempView.setCurrentValue(Rules.getInstance(this).getSettingsModel().getDunCurrentCount()); + // 左边盾值视图初始化(Java7分步写法,禁止链式调用) + DunTemperatureView tempViewLeft = (DunTemperatureView) findViewById(R.id.dun_temp_view_left); + tempViewLeft.setMaxValue(Rules.getInstance(this).getSettingsModel().getDunTotalCount()); + tempViewLeft.setCurrentValue(Rules.getInstance(this).getSettingsModel().getDunCurrentCount()); int[] customColors = new int[2]; customColors[0] = Color.parseColor("#FF3366FF"); @@ -355,7 +355,23 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv float[] positions = new float[2]; positions[0] = 0.0f; positions[1] = 1.0f; - tempView.setGradientColors(customColors, positions); + tempViewLeft.setGradientColors(customColors, positions); + // 文本放在温度条右侧(默认,可省略) + tempViewLeft.setTextPosition(true); + // 右边盾值视图初始化(Java7分步写法,禁止链式调用) + DunTemperatureView tempViewRight = (DunTemperatureView) findViewById(R.id.dun_temp_view_right); + tempViewRight.setMaxValue(Rules.getInstance(this).getSettingsModel().getDunTotalCount()); + tempViewRight.setCurrentValue(Rules.getInstance(this).getSettingsModel().getDunCurrentCount()); + +// int[] customColors = new int[2]; +// customColors[0] = Color.parseColor("#FF3366FF"); +// customColors[1] = Color.parseColor("#FF9900CC"); +// float[] positions = new float[2]; +// positions[0] = 0.0f; +// positions[1] = 1.0f; + tempViewRight.setGradientColors(customColors, positions); + // 文本放在温度条左侧 + tempViewRight.setTextPosition(false); LogUtils.d(TAG, "initUIAndLogic: 盾值视图初始化完成"); LogUtils.d(TAG, "===== initUIAndLogic: 初始化流程全部结束 ====="); } diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java index 3728e4c..782d9db 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java @@ -89,6 +89,9 @@ public class Rules { mSettingsModel.setDunCurrentCount(newDunCount); LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount)); saveDun(); + // 一键更新所有 DunTemperatureView 实例的盾值 + DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), mSettingsModel.getDunCurrentCount()); + SettingsActivity.notifyDunInfoUpdate(); } } @@ -228,7 +231,7 @@ public class Rules { // 返回校验结果 LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect)); // 一键更新所有 DunTemperatureView 实例的盾值 - DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), nDunCurrentCount); + DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), mSettingsModel.getDunCurrentCount()); return isConnect; } diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/views/DunTemperatureView.java b/contacts/src/main/java/cc/winboll/studio/contacts/views/DunTemperatureView.java index 02d98f6..7cce563 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/views/DunTemperatureView.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/views/DunTemperatureView.java @@ -11,27 +11,31 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.View; import cc.winboll.studio.libappbase.LogUtils; import java.util.WeakHashMap; /** - * @Author ZhanGSKen + * @Author ZhanGSKen&豆包大模型 * @Date 2025/03/19 14:04:20 * @Describe 云盾华氏度热力视图,垂直盾值温度视图控件(带颜色渐变+静态Handler更新) - * 采用绘图方式展示盾值温度,填充色随盾值比例渐变,底部显示 (当前盾值/最高盾值) 文本 - * 支持静态方法跨线程发送消息更新视图 + * 采用绘图方式展示盾值温度,填充色随盾值比例渐变,支持设置文本在温度条左侧/右侧,底部对齐竖排显示 + * 温度条宽度=5dp,文本区宽度固定=5dp,整体左右边距=0,无任何多余空白间距 */ public class DunTemperatureView extends View { // ====================== 常量定义区 ====================== public static final String TAG = "DunTemperatureView"; - // 控件默认尺寸 - private static final int DEFAULT_WIDTH = 80; + // 控件默认高度 private static final int DEFAULT_HEIGHT = 200; - // 文本预留高度 - private static final int TEXT_RESERVED_HEIGHT = 40; - // 填充区域内边距 - private static final int FILL_PADDING = 2; + // 温度条宽度(5dp)、文本区宽度(固定5dp) + private static final int THERMOMETER_WIDTH_DP = 5; + private static final int TEXT_AREA_WIDTH_DP = 5; + // 填充区域内边距(左右=0,上下=2避免贴边) + private static final int FILL_PADDING_HORIZONTAL = 0; + private static final int FILL_PADDING_VERTICAL = 2; + // 竖排文本字间距 + private static final float TEXT_CHAR_SPACING = 8f; // Handler消息标识 public static final int MSG_UPDATE_DUN_VALUE = 0x01; // 消息参数Key @@ -49,14 +53,17 @@ public class DunTemperatureView extends View { private Paint mThermometerPaint; private Paint mFillPaint; private Paint mTextPaint; - // 温度条相关参数 + // 尺寸参数(dp转px后的值) + private int mThermometerWidth; + private int mTextAreaWidth; private int mMaxValue = 100; // 最高盾值 private int mCurrentValue = 0; // 当前盾值 - private int mThermometerWidth = 40; // 温度条宽度 private RectF mThermometerRect; // 温度条矩形区域 // 渐变颜色配置(低→中→高 对应绿→黄→红) private int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED}; private float[] mGradientPositions = {0.0f, 0.5f, 1.0f}; + // 布局配置:true=文本在温度条右侧(默认),false=文本在温度条左侧 + private boolean isTextOnRight = true; // 其他颜色配置 private int mBorderColor = Color.parseColor("#FF444444"); private int mTextColor = Color.parseColor("#FF000000"); @@ -108,21 +115,27 @@ public class DunTemperatureView extends View { */ private void init() { LogUtils.d(TAG, "init: 开始初始化云盾温度视图控件"); - // 初始化温度条边框画笔 + // dp转px(适配不同分辨率) + mThermometerWidth = dp2px(getContext(), THERMOMETER_WIDTH_DP); + mTextAreaWidth = dp2px(getContext(), TEXT_AREA_WIDTH_DP); + LogUtils.d(TAG, "init: 温度条宽度5dp转px=" + mThermometerWidth + ",文本区宽度5dp转px=" + mTextAreaWidth); + + // 温度条边框画笔(宽度1px,适配5dp宽度) mThermometerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mThermometerPaint.setColor(mBorderColor); mThermometerPaint.setStyle(Paint.Style.STROKE); - mThermometerPaint.setStrokeWidth(2); + mThermometerPaint.setStrokeWidth(1); - // 初始化温度条填充画笔(支持渐变) + // 温度条填充画笔(支持渐变) mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mFillPaint.setStyle(Paint.Style.FILL); - // 初始化文本画笔 + // 文本画笔(适配5dp窄文本区,文字居中绘制) mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(mTextColor); - mTextPaint.setTextSize(30); + mTextPaint.setTextSize(18); // 缩小字号适配5dp窄文本区(避免文字超出) mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setFakeBoldText(true); // 文字加粗,提升窄区域可读性 // 初始化温度条矩形 mThermometerRect = new RectF(); @@ -132,6 +145,36 @@ public class DunTemperatureView extends View { LogUtils.d(TAG, "init: 云盾温度视图控件初始化完成,实例已加入缓存"); } + // ====================== 工具方法区 ====================== + /** + * dp 转 px(适配不同屏幕分辨率) + */ + private int dp2px(Context context, float dpValue) { + return (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + dpValue, + context.getResources().getDisplayMetrics() + ); + } + + // ====================== 对外控制方法区 ====================== + /** + * 设置文本相对于温度条的位置 + * @param isOnRight true=文本在温度条右侧(默认),false=文本在温度条左侧 + */ + public void setTextPosition(boolean isOnRight) { + this.isTextOnRight = isOnRight; + invalidate(); // 刷新布局绘制 + } + + /** + * 获取当前文本位置配置 + * @return true=右侧,false=左侧 + */ + public boolean isTextOnRight() { + return isTextOnRight; + } + // ====================== 对外静态方法区 ====================== /** * 静态外部方法:发送消息更新所有 DunTemperatureView 实例的盾值 @@ -165,7 +208,6 @@ public class DunTemperatureView extends View { this.mMaxValue = maxValue; // 限制当前值不超过最大值 mCurrentValue = Math.min(mCurrentValue, maxValue); - LogUtils.d(TAG, "setMaxValue: 最高盾值设置为" + maxValue + ",当前值校准为" + mCurrentValue); invalidate(); // 重绘控件 } @@ -260,69 +302,92 @@ public class DunTemperatureView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // 设置控件默认尺寸 - int width = measureSize(DEFAULT_WIDTH, widthMeasureSpec); + // 强制控件整体左右内边距=0,彻底消除外部边距 + setPadding(0, getPaddingTop(), 0, getPaddingBottom()); + + // 控件宽度=温度条宽度 + 文本区宽度(均为5dp转px,无额外空白) + int defaultWidth = mThermometerWidth + mTextAreaWidth; + int width = measureSize(defaultWidth, widthMeasureSpec); int height = measureSize(DEFAULT_HEIGHT, heightMeasureSpec); setMeasuredDimension(width, height); - // 计算温度条矩形区域(居中绘制) - int paddingLeft = getPaddingLeft(); - int paddingRight = getPaddingRight(); - int paddingTop = getPaddingTop(); - int paddingBottom = getPaddingBottom(); + // 根据文本位置配置,计算温度条矩形坐标 + float thermometerLeft, thermometerRight; + if (isTextOnRight) { + // 文本在右侧:温度条靠左,右接文本区 + thermometerLeft = 0; + thermometerRight = thermometerLeft + mThermometerWidth; + } else { + // 文本在左侧:温度条靠右,左接文本区 + thermometerLeft = width - mThermometerWidth; + thermometerRight = width; + } + float thermometerTop = getPaddingTop(); + float thermometerBottom = height - getPaddingBottom(); + mThermometerRect.set(thermometerLeft, thermometerTop, thermometerRight, thermometerBottom); - int contentWidth = width - paddingLeft - paddingRight; - int contentHeight = height - paddingTop - paddingBottom - TEXT_RESERVED_HEIGHT; - - float left = paddingLeft + (contentWidth - mThermometerWidth) / 2f; - float right = left + mThermometerWidth; - float top = paddingTop; - float bottom = top + contentHeight; - - mThermometerRect.set(left, top, right, bottom); - LogUtils.v(TAG, "onMeasure: 温度条矩形区域设置为" + mThermometerRect.toShortString()); + LogUtils.v(TAG, "onMeasure: 文本位置=" + (isTextOnRight ? "右侧" : "左侧") + ",控件尺寸=" + width + "x" + height + ",温度条区域=" + mThermometerRect.toShortString()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - // 绘制温度条边框 - canvas.drawRoundRect(mThermometerRect, 10, 10, mThermometerPaint); + // 1. 绘制温度条边框(5dp宽度,小圆角适配) + canvas.drawRoundRect(mThermometerRect, 3, 3, mThermometerPaint); - // 计算填充高度(根据当前值占最大值的比例) + // 2. 计算填充高度(根据当前值占最大值的比例) float fillRatio = (float) mCurrentValue / mMaxValue; float fillHeight = mThermometerRect.height() * fillRatio; float fillTop = mThermometerRect.bottom - fillHeight; - // 绘制渐变填充部分 + // 3. 绘制渐变填充部分(左右贴边框,无间距) if (fillHeight > 0) { RectF fillRect = new RectF( - mThermometerRect.left + FILL_PADDING, - fillTop, - mThermometerRect.right - FILL_PADDING, - mThermometerRect.bottom + mThermometerRect.left + FILL_PADDING_HORIZONTAL, + fillTop + FILL_PADDING_VERTICAL, + mThermometerRect.right - FILL_PADDING_HORIZONTAL, + mThermometerRect.bottom - FILL_PADDING_VERTICAL ); - // 创建线性渐变(从下到上,对应盾值低到高) LinearGradient gradient = new LinearGradient( - fillRect.centerX(), fillRect.bottom, - fillRect.centerX(), fillRect.top, - mGradientColors, - mGradientPositions, - Shader.TileMode.CLAMP + fillRect.centerX(), fillRect.bottom, + fillRect.centerX(), fillRect.top, + mGradientColors, + mGradientPositions, + Shader.TileMode.CLAMP ); mFillPaint.setShader(gradient); - canvas.drawRoundRect(fillRect, 8, 8, mFillPaint); - // 用完渐变后清空shader,避免影响后续绘制 + canvas.drawRoundRect(fillRect, 2, 2, mFillPaint); mFillPaint.setShader(null); - LogUtils.v(TAG, "onDraw: 渐变填充绘制完成,填充高度=" + fillHeight + ",比例=" + fillRatio); } - // 绘制底部文本 (当前盾值/最高盾值) - String text = String.format("(%d/%d)", mCurrentValue, mMaxValue); - float textX = getWidth() / 2f; - float textY = mThermometerRect.bottom + 30; - canvas.drawText(text, textX, textY, mTextPaint); - LogUtils.v(TAG, "onDraw: 底部文本绘制完成,内容=" + text); + // 4. 绘制文本(5dp固定宽度文本区,底部对齐竖排,文字居中) + String text = String.format("%d/%d", mCurrentValue, mMaxValue); + if (text.isEmpty()) return; + + float textBaseX; + if (isTextOnRight) { + // 文本在右侧:X=温度条右边缘 + 文本区宽度的一半(紧贴温度条) + textBaseX = mThermometerRect.right + (mTextAreaWidth / 2f); + } else { + // 文本在左侧:X=文本区宽度的一半(紧贴控件左边缘) + textBaseX = mTextAreaWidth / 2f; + } + + // 文本绘制参数(适配5dp窄区域,缩小字间距提升紧凑度) + float singleCharHeight = mTextPaint.getTextSize() + (TEXT_CHAR_SPACING - 2f); // 字间距减为6f + float totalTextHeight = (singleCharHeight * text.length()) - (TEXT_CHAR_SPACING - 2f); + Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); + float charBottomOffset = fontMetrics.bottom; + + // 文本起始Y(底部对齐控件底部,无间距) + float startTextY = getHeight() - getPaddingBottom() - charBottomOffset; + + // 逐字竖排绘制(文字居中于5dp文本区,无超出) + for (int i = 0; i < text.length(); i++) { + char singleChar = text.charAt(i); + float currentTextY = startTextY - (i * singleCharHeight); + canvas.drawText(String.valueOf(singleChar), textBaseX, currentTextY, mTextPaint); + } } } diff --git a/contacts/src/main/res/layout/activity_main.xml b/contacts/src/main/res/layout/activity_main.xml index 9b30a71..f06ae70 100644 --- a/contacts/src/main/res/layout/activity_main.xml +++ b/contacts/src/main/res/layout/activity_main.xml @@ -21,12 +21,13 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1.0" - android:padding="10dp"> + android:paddingTop="10dp" + android:paddingBottom="10dp"> + android:layout_toRightOf="@id/dun_temp_view_left" + android:layout_toLeftOf="@id/dun_temp_view_right"> + +