更新盾力热度条视图。修复一些盾值更新问题。

This commit is contained in:
2025-12-13 01:25:26 +08:00
parent f591db6611
commit b6a820b281
5 changed files with 161 additions and 70 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #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 stageCount=1
libraryProject= libraryProject=
baseVersion=15.12 baseVersion=15.12
publishVersion=15.12.0 publishVersion=15.12.0
buildCount=61 buildCount=75
baseBetaVersion=15.12.1 baseBetaVersion=15.12.1

View File

@@ -344,10 +344,10 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv
initPhoneStateListener(); initPhoneStateListener();
LogUtils.d(TAG, "initUIAndLogic: 电话状态监听器初始化完成"); LogUtils.d(TAG, "initUIAndLogic: 电话状态监听器初始化完成");
// 6. 盾值视图初始化Java7分步写法禁止链式调用 // 左边盾值视图初始化Java7分步写法禁止链式调用
DunTemperatureView tempView = (DunTemperatureView) findViewById(R.id.dun_temp_view); DunTemperatureView tempViewLeft = (DunTemperatureView) findViewById(R.id.dun_temp_view_left);
tempView.setMaxValue(Rules.getInstance(this).getSettingsModel().getDunTotalCount()); tempViewLeft.setMaxValue(Rules.getInstance(this).getSettingsModel().getDunTotalCount());
tempView.setCurrentValue(Rules.getInstance(this).getSettingsModel().getDunCurrentCount()); tempViewLeft.setCurrentValue(Rules.getInstance(this).getSettingsModel().getDunCurrentCount());
int[] customColors = new int[2]; int[] customColors = new int[2];
customColors[0] = Color.parseColor("#FF3366FF"); customColors[0] = Color.parseColor("#FF3366FF");
@@ -355,7 +355,23 @@ public final class MainActivity extends WinBollActivity implements IWinBoLLActiv
float[] positions = new float[2]; float[] positions = new float[2];
positions[0] = 0.0f; positions[0] = 0.0f;
positions[1] = 1.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: 盾值视图初始化完成");
LogUtils.d(TAG, "===== initUIAndLogic: 初始化流程全部结束 ====="); LogUtils.d(TAG, "===== initUIAndLogic: 初始化流程全部结束 =====");
} }

View File

@@ -89,6 +89,9 @@ public class Rules {
mSettingsModel.setDunCurrentCount(newDunCount); mSettingsModel.setDunCurrentCount(newDunCount);
LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount)); LogUtils.d(TAG, String.format("设置防御值为%d", newDunCount));
saveDun(); saveDun();
// 一键更新所有 DunTemperatureView 实例的盾值
DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), mSettingsModel.getDunCurrentCount());
SettingsActivity.notifyDunInfoUpdate(); SettingsActivity.notifyDunInfoUpdate();
} }
} }
@@ -228,7 +231,7 @@ public class Rules {
// 返回校验结果 // 返回校验结果
LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect)); LogUtils.d(TAG, String.format("返回校验结果 isConnect == %s", isConnect));
// 一键更新所有 DunTemperatureView 实例的盾值 // 一键更新所有 DunTemperatureView 实例的盾值
DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), nDunCurrentCount); DunTemperatureView.updateDunValue(mSettingsModel.getDunTotalCount(), mSettingsModel.getDunCurrentCount());
return isConnect; return isConnect;
} }

View File

@@ -11,27 +11,31 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View; import android.view.View;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import java.util.WeakHashMap; import java.util.WeakHashMap;
/** /**
* @Author ZhanGSKen<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/03/19 14:04:20 * @Date 2025/03/19 14:04:20
* @Describe 云盾华氏度热力视图,垂直盾值温度视图控件(带颜色渐变+静态Handler更新 * @Describe 云盾华氏度热力视图,垂直盾值温度视图控件(带颜色渐变+静态Handler更新
* 采用绘图方式展示盾值温度,填充色随盾值比例渐变,底部显示 (当前盾值/最高盾值) 文本 * 采用绘图方式展示盾值温度,填充色随盾值比例渐变,支持设置文本在温度条左侧/右侧,底部对齐竖排显示
* 支持静态方法跨线程发送消息更新视图 * 温度条宽度=5dp文本区宽度固定=5dp整体左右边距=0无任何多余空白间距
*/ */
public class DunTemperatureView extends View { public class DunTemperatureView extends View {
// ====================== 常量定义区 ====================== // ====================== 常量定义区 ======================
public static final String TAG = "DunTemperatureView"; public static final String TAG = "DunTemperatureView";
// 控件默认尺寸 // 控件默认高度
private static final int DEFAULT_WIDTH = 80;
private static final int DEFAULT_HEIGHT = 200; private static final int DEFAULT_HEIGHT = 200;
// 文本预留高度 // 温度条宽度5dp、文本区宽度固定5dp
private static final int TEXT_RESERVED_HEIGHT = 40; private static final int THERMOMETER_WIDTH_DP = 5;
// 填充区域内边距 private static final int TEXT_AREA_WIDTH_DP = 5;
private static final int FILL_PADDING = 2; // 填充区域内边距(左右=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消息标识 // Handler消息标识
public static final int MSG_UPDATE_DUN_VALUE = 0x01; public static final int MSG_UPDATE_DUN_VALUE = 0x01;
// 消息参数Key // 消息参数Key
@@ -49,14 +53,17 @@ public class DunTemperatureView extends View {
private Paint mThermometerPaint; private Paint mThermometerPaint;
private Paint mFillPaint; private Paint mFillPaint;
private Paint mTextPaint; private Paint mTextPaint;
// 温度条相关参数 // 尺寸参数dp转px后的值
private int mThermometerWidth;
private int mTextAreaWidth;
private int mMaxValue = 100; // 最高盾值 private int mMaxValue = 100; // 最高盾值
private int mCurrentValue = 0; // 当前盾值 private int mCurrentValue = 0; // 当前盾值
private int mThermometerWidth = 40; // 温度条宽度
private RectF mThermometerRect; // 温度条矩形区域 private RectF mThermometerRect; // 温度条矩形区域
// 渐变颜色配置(低→中→高 对应绿→黄→红) // 渐变颜色配置(低→中→高 对应绿→黄→红)
private int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED}; private int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED};
private float[] mGradientPositions = {0.0f, 0.5f, 1.0f}; private float[] mGradientPositions = {0.0f, 0.5f, 1.0f};
// 布局配置true=文本在温度条右侧默认false=文本在温度条左侧
private boolean isTextOnRight = true;
// 其他颜色配置 // 其他颜色配置
private int mBorderColor = Color.parseColor("#FF444444"); private int mBorderColor = Color.parseColor("#FF444444");
private int mTextColor = Color.parseColor("#FF000000"); private int mTextColor = Color.parseColor("#FF000000");
@@ -108,21 +115,27 @@ public class DunTemperatureView extends View {
*/ */
private void init() { private void init() {
LogUtils.d(TAG, "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 = new Paint(Paint.ANTI_ALIAS_FLAG);
mThermometerPaint.setColor(mBorderColor); mThermometerPaint.setColor(mBorderColor);
mThermometerPaint.setStyle(Paint.Style.STROKE); mThermometerPaint.setStyle(Paint.Style.STROKE);
mThermometerPaint.setStrokeWidth(2); mThermometerPaint.setStrokeWidth(1);
// 初始化温度条填充画笔(支持渐变) // 温度条填充画笔(支持渐变)
mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFillPaint.setStyle(Paint.Style.FILL); mFillPaint.setStyle(Paint.Style.FILL);
// 初始化文本画笔 // 文本画笔适配5dp窄文本区文字居中绘制
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(mTextColor); mTextPaint.setColor(mTextColor);
mTextPaint.setTextSize(30); mTextPaint.setTextSize(18); // 缩小字号适配5dp窄文本区避免文字超出
mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setFakeBoldText(true); // 文字加粗,提升窄区域可读性
// 初始化温度条矩形 // 初始化温度条矩形
mThermometerRect = new RectF(); mThermometerRect = new RectF();
@@ -132,6 +145,36 @@ public class DunTemperatureView extends View {
LogUtils.d(TAG, "init: 云盾温度视图控件初始化完成,实例已加入缓存"); 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 实例的盾值 * 静态外部方法:发送消息更新所有 DunTemperatureView 实例的盾值
@@ -165,7 +208,6 @@ public class DunTemperatureView extends View {
this.mMaxValue = maxValue; this.mMaxValue = maxValue;
// 限制当前值不超过最大值 // 限制当前值不超过最大值
mCurrentValue = Math.min(mCurrentValue, maxValue); mCurrentValue = Math.min(mCurrentValue, maxValue);
LogUtils.d(TAG, "setMaxValue: 最高盾值设置为" + maxValue + ",当前值校准为" + mCurrentValue);
invalidate(); // 重绘控件 invalidate(); // 重绘控件
} }
@@ -260,69 +302,92 @@ public class DunTemperatureView extends View {
@Override @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 设置控件默认尺寸 // 强制控件整体左右内边距=0彻底消除外部边距
int width = measureSize(DEFAULT_WIDTH, widthMeasureSpec); setPadding(0, getPaddingTop(), 0, getPaddingBottom());
// 控件宽度=温度条宽度 + 文本区宽度均为5dp转px无额外空白
int defaultWidth = mThermometerWidth + mTextAreaWidth;
int width = measureSize(defaultWidth, widthMeasureSpec);
int height = measureSize(DEFAULT_HEIGHT, heightMeasureSpec); int height = measureSize(DEFAULT_HEIGHT, heightMeasureSpec);
setMeasuredDimension(width, height); setMeasuredDimension(width, height);
// 计算温度条矩形区域(居中绘制) // 根据文本位置配置,计算温度条矩形坐标
int paddingLeft = getPaddingLeft(); float thermometerLeft, thermometerRight;
int paddingRight = getPaddingRight(); if (isTextOnRight) {
int paddingTop = getPaddingTop(); // 文本在右侧:温度条靠左,右接文本区
int paddingBottom = getPaddingBottom(); 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; LogUtils.v(TAG, "onMeasure: 文本位置=" + (isTextOnRight ? "右侧" : "左侧") + ",控件尺寸=" + width + "x" + height + ",温度条区域=" + mThermometerRect.toShortString());
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());
} }
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
// 绘制温度条边框 // 1. 绘制温度条边框5dp宽度小圆角适配
canvas.drawRoundRect(mThermometerRect, 10, 10, mThermometerPaint); canvas.drawRoundRect(mThermometerRect, 3, 3, mThermometerPaint);
// 计算填充高度(根据当前值占最大值的比例) // 2. 计算填充高度(根据当前值占最大值的比例)
float fillRatio = (float) mCurrentValue / mMaxValue; float fillRatio = (float) mCurrentValue / mMaxValue;
float fillHeight = mThermometerRect.height() * fillRatio; float fillHeight = mThermometerRect.height() * fillRatio;
float fillTop = mThermometerRect.bottom - fillHeight; float fillTop = mThermometerRect.bottom - fillHeight;
// 绘制渐变填充部分 // 3. 绘制渐变填充部分(左右贴边框,无间距)
if (fillHeight > 0) { if (fillHeight > 0) {
RectF fillRect = new RectF( RectF fillRect = new RectF(
mThermometerRect.left + FILL_PADDING, mThermometerRect.left + FILL_PADDING_HORIZONTAL,
fillTop, fillTop + FILL_PADDING_VERTICAL,
mThermometerRect.right - FILL_PADDING, mThermometerRect.right - FILL_PADDING_HORIZONTAL,
mThermometerRect.bottom mThermometerRect.bottom - FILL_PADDING_VERTICAL
); );
// 创建线性渐变(从下到上,对应盾值低到高)
LinearGradient gradient = new LinearGradient( LinearGradient gradient = new LinearGradient(
fillRect.centerX(), fillRect.bottom, fillRect.centerX(), fillRect.bottom,
fillRect.centerX(), fillRect.top, fillRect.centerX(), fillRect.top,
mGradientColors, mGradientColors,
mGradientPositions, mGradientPositions,
Shader.TileMode.CLAMP Shader.TileMode.CLAMP
); );
mFillPaint.setShader(gradient); mFillPaint.setShader(gradient);
canvas.drawRoundRect(fillRect, 8, 8, mFillPaint); canvas.drawRoundRect(fillRect, 2, 2, mFillPaint);
// 用完渐变后清空shader避免影响后续绘制
mFillPaint.setShader(null); mFillPaint.setShader(null);
LogUtils.v(TAG, "onDraw: 渐变填充绘制完成,填充高度=" + fillHeight + ",比例=" + fillRatio);
} }
// 绘制底部文本 (当前盾值/最高盾值) // 4. 绘制文本5dp固定宽度文本区底部对齐竖排文字居中
String text = String.format("(%d/%d)", mCurrentValue, mMaxValue); String text = String.format("%d/%d", mCurrentValue, mMaxValue);
float textX = getWidth() / 2f; if (text.isEmpty()) return;
float textY = mThermometerRect.bottom + 30;
canvas.drawText(text, textX, textY, mTextPaint); float textBaseX;
LogUtils.v(TAG, "onDraw: 底部文本绘制完成,内容=" + text); 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);
}
} }
} }

View File

@@ -21,12 +21,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1.0" android:layout_weight="1.0"
android:padding="10dp"> android:paddingTop="10dp"
android:paddingBottom="10dp">
<cc.winboll.studio.contacts.views.DunTemperatureView <cc.winboll.studio.contacts.views.DunTemperatureView
android:layout_width="50dp" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/dun_temp_view" android:id="@+id/dun_temp_view_left"
android:layout_alignParentLeft="true"/> android:layout_alignParentLeft="true"/>
<LinearLayout <LinearLayout
@@ -34,8 +35,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/activitymainLinearLayout1" android:id="@+id/activitymainLinearLayout1"
android:layout_toRightOf="@id/dun_temp_view" android:layout_toRightOf="@id/dun_temp_view_left"
android:layout_alignParentRight="true"> android:layout_toLeftOf="@id/dun_temp_view_right">
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -50,6 +51,12 @@
</LinearLayout> </LinearLayout>
<cc.winboll.studio.contacts.views.DunTemperatureView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/dun_temp_view_right"
android:layout_alignParentRight="true"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>