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

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
#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

View File

@@ -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: 初始化流程全部结束 =====");
}

View File

@@ -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;
}

View File

@@ -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<zhangsken@qq.com>
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @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);
}
}
}

View File

@@ -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">
<cc.winboll.studio.contacts.views.DunTemperatureView
android:layout_width="50dp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/dun_temp_view"
android:id="@+id/dun_temp_view_left"
android:layout_alignParentLeft="true"/>
<LinearLayout
@@ -34,8 +35,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainLinearLayout1"
android:layout_toRightOf="@id/dun_temp_view"
android:layout_alignParentRight="true">
android:layout_toRightOf="@id/dun_temp_view_left"
android:layout_toLeftOf="@id/dun_temp_view_right">
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
@@ -50,6 +51,12 @@
</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>
</LinearLayout>