修复电量进度条拉动时无响应问题

This commit is contained in:
2025-12-17 14:50:21 +08:00
parent dbcb5259d9
commit 1e9a6adc88
3 changed files with 475 additions and 280 deletions

View File

@@ -5,7 +5,6 @@ import android.content.Context;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.App;
import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.dialogs.YesNoAlertDialog;
import cc.winboll.studio.powerbell.models.AppConfigBean;
import cc.winboll.studio.powerbell.models.ControlCenterServiceBean;
import cc.winboll.studio.powerbell.services.ControlCenterService;
@@ -24,9 +23,6 @@ public class AppConfigUtils {
private static final int MAX_REMINDER_VALUE = 100; // 提醒阈值最大值
private static final int MIN_INTERVAL_TIME = 1000; // 最小提醒间隔ms
private static final int MIN_DETECT_INTERVAL = 500; // 最小电量检测间隔ms
private static final String CONFIRM_TITLE = "配置变更确认";
private static final String CONFIRM_MSG = "是否确认更改该配置?";
private static final String SAVE_ALL_CONFIRM_MSG = "是否保存当前所有配置?";
// ======================== 静态成员(单例实例,严格控制初始化)========================
private static AppConfigUtils sInstance; // 单例实例(私有,禁止外部直接创建)
@@ -104,7 +100,7 @@ public class AppConfigUtils {
}
/**
* 保存应用配置(内部核心方法,无弹窗,直接持久化,同步通知服务+Activity
* 保存应用配置(内部核心方法,直接持久化,同步通知服务+Activity
*/
private void saveAppConfig() {
AppConfigBean.saveBean(mContext, mAppConfigBean);
@@ -115,7 +111,7 @@ public class AppConfigUtils {
}
/**
* 保存服务配置(内部核心方法,无弹窗,直接持久化)
* 保存服务配置(内部核心方法,直接持久化)
*/
private void saveServiceConfig() {
mServiceConfigBean.setIsEnableService(mIsServiceEnabled);
@@ -124,59 +120,26 @@ public class AppConfigUtils {
}
// ======================== 通用工具方法(抽取复用,减少冗余)========================
/**
* 配置变更确认弹窗所有配置修改统一调用适配API30对话框规范
* @param activity 宿主Activity非空校验
* @param confirmAction 确认后执行的逻辑
*/
private void showConfigConfirmDialog(Activity activity, final Runnable confirmAction) {
if (activity == null || activity.isFinishing()) {
LogUtils.e(TAG, "showConfigConfirmDialog: Activity无效弹窗显示失败");
return;
}
YesNoAlertDialog.show(activity, CONFIRM_TITLE, CONFIRM_MSG, new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
LogUtils.d(TAG, "showConfigConfirmDialog: 用户确认配置变更");
confirmAction.run();
}
@Override
public void onNo() {
LogUtils.d(TAG, "showConfigConfirmDialog: 用户取消配置变更,刷新配置显示");
MainActivity.reloadAppConfig();
}
});
}
// ======================== 服务开关配置方法(单独归类,逻辑聚焦)========================
/**
* 设置服务开关状态(带弹窗确认适配API30后台服务启停规范
* @param activity 宿主Activity
* 设置服务开关状态(直接生效,无弹窗适配API30后台服务启停规范
* @param isEnabled 目标状态true=开启false=关闭)
*/
public void setServiceEnabled(Activity activity, final boolean isEnabled) {
public void setServiceEnabled(final boolean isEnabled) {
if (isEnabled == mIsServiceEnabled) {
LogUtils.d(TAG, "setServiceEnabled: 服务状态无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mIsServiceEnabled = isEnabled;
saveServiceConfig();
// 启停服务适配API30后台服务限制
if (mIsServiceEnabled) {
LogUtils.d(TAG, "setServiceEnabled: 启动ControlCenterService");
ControlCenterService.startControlCenterService(mContext);
} else {
LogUtils.d(TAG, "setServiceEnabled: 停止ControlCenterService");
ControlCenterService.stopControlCenterService(mContext);
}
}
});
mIsServiceEnabled = isEnabled;
saveServiceConfig();
// 启停服务适配API30后台服务限制
if (mIsServiceEnabled) {
LogUtils.d(TAG, "setServiceEnabled: 启动ControlCenterService");
ControlCenterService.startControlCenterService(mContext);
} else {
LogUtils.d(TAG, "setServiceEnabled: 停止ControlCenterService");
ControlCenterService.stopControlCenterService(mContext);
}
}
/**
@@ -191,23 +154,17 @@ public class AppConfigUtils {
// ======================== 充电提醒配置方法(单独归类,逻辑聚焦)========================
/**
* 设置充电提醒开关状态(带弹窗确认
* @param activity 宿主Activity
* 设置充电提醒开关状态(直接生效,无弹窗
* @param isEnabled 目标状态true=开启false=关闭)
*/
public void setChargeReminderEnabled(Activity activity, final boolean isEnabled) {
public void setChargeReminderEnabled(final boolean isEnabled) {
if (isEnabled == mAppConfigBean.isEnableChargeReminder()) {
LogUtils.d(TAG, "setChargeReminderEnabled: 充电提醒状态无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setEnableChargeReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "setChargeReminderEnabled: 充电提醒状态更新为=" + (isEnabled ? "开启" : "关闭"));
}
});
mAppConfigBean.setEnableChargeReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "setChargeReminderEnabled: 充电提醒状态更新为=" + (isEnabled ? "开启" : "关闭"));
}
/**
@@ -221,24 +178,18 @@ public class AppConfigUtils {
}
/**
* 设置充电提醒阈值(带弹窗确认自动校准范围适配API30数据安全
* @param activity 宿主Activity
* 设置充电提醒阈值(直接生效,无弹窗自动校准范围适配API30数据安全
* @param value 目标阈值自动校准0-100
*/
public void setChargeReminderValue(Activity activity, final int value) {
public void setChargeReminderValue(final int value) {
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getChargeReminderValue()) {
LogUtils.d(TAG, "setChargeReminderValue: 充电提醒阈值无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setChargeReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "setChargeReminderValue: 充电提醒阈值更新为=" + calibratedValue + "%");
}
});
mAppConfigBean.setChargeReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "setChargeReminderValue: 充电提醒阈值更新为=" + calibratedValue + "%");
}
/**
@@ -254,23 +205,17 @@ public class AppConfigUtils {
// ======================== 耗电提醒配置方法(单独归类,逻辑聚焦)========================
/**
* 设置耗电提醒开关状态(带弹窗确认
* @param activity 宿主Activity
* 设置耗电提醒开关状态(直接生效,无弹窗
* @param isEnabled 目标状态true=开启false=关闭)
*/
public void setUsageReminderEnabled(Activity activity, final boolean isEnabled) {
public void setUsageReminderEnabled(final boolean isEnabled) {
if (isEnabled == mAppConfigBean.isEnableUsageReminder()) {
LogUtils.d(TAG, "setUsageReminderEnabled: 耗电提醒状态无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setEnableUsageReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "setUsageReminderEnabled: 耗电提醒状态更新为=" + (isEnabled ? "开启" : "关闭"));
}
});
mAppConfigBean.setEnableUsageReminder(isEnabled);
saveAppConfig();
LogUtils.d(TAG, "setUsageReminderEnabled: 耗电提醒状态更新为=" + (isEnabled ? "开启" : "关闭"));
}
/**
@@ -284,24 +229,18 @@ public class AppConfigUtils {
}
/**
* 设置耗电提醒阈值(带弹窗确认,自动校准范围,适配小米手机电量跳变)
* @param activity 宿主Activity
* 设置耗电提醒阈值(直接生效,无弹窗,自动校准范围,适配小米手机电量跳变)
* @param value 目标阈值自动校准0-100
*/
public void setUsageReminderValue(Activity activity, final int value) {
public void setUsageReminderValue(final int value) {
final int calibratedValue = Math.min(Math.max(value, MIN_REMINDER_VALUE), MAX_REMINDER_VALUE);
if (calibratedValue == mAppConfigBean.getUsageReminderValue()) {
LogUtils.d(TAG, "setUsageReminderValue: 耗电提醒阈值无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setUsageReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "setUsageReminderValue: 耗电提醒阈值更新为=" + calibratedValue + "%");
}
});
mAppConfigBean.setUsageReminderValue(calibratedValue);
saveAppConfig();
LogUtils.d(TAG, "setUsageReminderValue: 耗电提醒阈值更新为=" + calibratedValue + "%");
}
/**
@@ -364,26 +303,20 @@ public class AppConfigUtils {
}
// ======================== 间隔配置方法(持久化存储,带弹窗确认========================
// ======================== 间隔配置方法(持久化存储,直接生效,无弹窗========================
/**
* 设置提醒间隔时间(带弹窗确认自动校准最小1000ms
* @param activity 宿主Activity
* 设置提醒间隔时间(直接生效,无弹窗自动校准最小1000ms
* @param interval 目标间隔单位ms
*/
public void setReminderIntervalTime(Activity activity, final int interval) {
public void setReminderIntervalTime(final int interval) {
final int calibratedInterval = Math.max(interval, MIN_INTERVAL_TIME);
if (calibratedInterval == mAppConfigBean.getReminderIntervalTime()) {
LogUtils.d(TAG, "setReminderIntervalTime: 提醒间隔无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setReminderIntervalTime(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "setReminderIntervalTime: 提醒间隔更新为=" + calibratedInterval + "ms");
}
});
mAppConfigBean.setReminderIntervalTime(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "setReminderIntervalTime: 提醒间隔更新为=" + calibratedInterval + "ms");
}
/**
@@ -397,24 +330,18 @@ public class AppConfigUtils {
}
/**
* 设置电量检测间隔(带弹窗确认自动校准最小500ms与RemindThread同步
* @param activity 宿主Activity
* 设置电量检测间隔(直接生效,无弹窗自动校准最小500ms与RemindThread同步
* @param interval 目标间隔单位ms
*/
public void setBatteryDetectInterval(Activity activity, final int interval) {
public void setBatteryDetectInterval(final int interval) {
final int calibratedInterval = Math.max(interval, MIN_DETECT_INTERVAL);
if (calibratedInterval == mAppConfigBean.getBatteryDetectInterval()) {
LogUtils.d(TAG, "setBatteryDetectInterval: 检测间隔无变化,无需操作");
return;
}
showConfigConfirmDialog(activity, new Runnable() {
@Override
public void run() {
mAppConfigBean.setBatteryDetectInterval(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "setBatteryDetectInterval: 电量检测间隔更新为=" + calibratedInterval + "ms");
}
});
mAppConfigBean.setBatteryDetectInterval(calibratedInterval);
saveAppConfig();
LogUtils.d(TAG, "setBatteryDetectInterval: 电量检测间隔更新为=" + calibratedInterval + "ms");
}
/**
@@ -428,30 +355,14 @@ public class AppConfigUtils {
}
// ======================== 外部配置操作入口(带用户确认供Activity调用========================
// ======================== 外部配置操作入口(直接生效,无弹窗========================
/**
* 保存所有配置(带弹窗确认MainActivity专用入口)
* @param activity MainActivity实例
* 保存所有配置(直接生效,无弹窗,主动保存入口)
*/
public void saveConfigWithConfirm(final MainActivity activity) {
if (activity == null || activity.isFinishing()) {
LogUtils.e(TAG, "saveConfigWithConfirm: Activity无效保存配置失败");
return;
}
YesNoAlertDialog.show(activity, CONFIRM_TITLE, SAVE_ALL_CONFIRM_MSG, new YesNoAlertDialog.OnDialogResultListener() {
@Override
public void onYes() {
saveAppConfig();
LogUtils.d(TAG, "saveConfigWithConfirm: 用户确认保存所有配置");
}
@Override
public void onNo() {
LogUtils.d(TAG, "saveConfigWithConfirm: 用户取消保存,重新加载配置");
loadAllConfig();
MainActivity.reloadAppConfig();
}
});
public void saveAllConfig() {
saveAppConfig();
saveServiceConfig();
LogUtils.d(TAG, "saveAllConfig: 所有配置保存完成");
}
/**

View File

@@ -1,7 +1,9 @@
package cc.winboll.studio.powerbell.views;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
@@ -9,24 +11,23 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.Switch;
import android.widget.TextView;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.R;
import cc.winboll.studio.powerbell.utils.AppConfigUtils;
import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 13:14
* @Describe 主页面核心视图封装类:统一管理视图绑定、数据更新、事件监听,解耦 Activity 逻辑
* 适配Java7 | API30 | 小米手机,优化性能与资源回收,杜绝内存泄漏
* 适配Java7 | API30 | 小米手机,优化性能与资源回收,杜绝内存泄漏,配置变更确认对话框
*/
public class MainContentView {
// ======================== 静态常量(置顶,唯一标识)========================
public static final String TAG = "MainContentView";
// ======================== 核心成员变量(按「依赖→视图→内部资源」排序)========================
// ======================== 核心成员变量(按「依赖→视图→内部资源→对话框」排序)========================
// 外部依赖实例(生命周期关联,优先声明)
private Context mContext;
private AppConfigUtils mAppConfigUtils;
@@ -48,7 +49,7 @@ public class MainContentView {
public TextView tvChargeReminderValue;
public TextView tvUsageReminderValue;
public TextView tvCurrentBatteryValue;
// 进度条控件
// 进度条控件(使用自定义 VerticalSeekBar
public VerticalSeekBar sbChargeReminder;
public VerticalSeekBar sbUsageReminder;
// 图标显示控件
@@ -61,6 +62,46 @@ public class MainContentView {
private BatteryDrawable mChargeReminderBatteryDrawable;
private BatteryDrawable mUsageReminderBatteryDrawable;
// 配置变更确认对话框(单例复用,避免重复创建)
private AlertDialog mConfigConfirmDialog;
// 临时存储变更数据(对话框确认前缓存,取消时恢复)
private TempConfigData mTempConfigData;
// 对话框状态锁(避免快速点击重复弹窗)
private boolean isDialogShowing = false;
// ======================== 临时配置数据实体(缓存变更信息,取消时恢复)========================
private static class TempConfigData {
// 变更类型(区分开关/进度条,避免混淆)
int changeType;
// 原始值(取消时恢复用)
boolean originalBooleanValue;
int originalIntValue;
// 变更后的值(确认时保存用)
boolean newBooleanValue;
int newIntValue;
// 构造方法(开关类型)
TempConfigData(int changeType, boolean originalValue, boolean newValue) {
this.changeType = changeType;
this.originalBooleanValue = originalValue;
this.newBooleanValue = newValue;
}
// 构造方法(进度条类型)
TempConfigData(int changeType, int originalValue, int newValue) {
this.changeType = changeType;
this.originalIntValue = originalValue;
this.newIntValue = newValue;
}
}
// 变更类型常量(区分不同控件,精准处理逻辑)
private static final int CHANGE_TYPE_CHARGE_SWITCH = 1;
private static final int CHANGE_TYPE_USAGE_SWITCH = 2;
private static final int CHANGE_TYPE_SERVICE_SWITCH = 3;
private static final int CHANGE_TYPE_CHARGE_SEEKBAR = 4;
private static final int CHANGE_TYPE_USAGE_SEEKBAR = 5;
// ======================== 构造方法(初始化入口,逻辑闭环)========================
public MainContentView(Context context, View rootView, OnViewActionListener actionListener) {
LogUtils.d(TAG, "constructor: 开始初始化");
@@ -73,6 +114,7 @@ public class MainContentView {
// 执行核心初始化流程(按顺序执行,避免依赖空指针)
bindViews(rootView);
initBatteryDrawables();
initConfirmDialog();
bindViewListeners();
LogUtils.d(TAG, "constructor: 整体初始化完成");
@@ -99,7 +141,7 @@ public class MainContentView {
tvChargeReminderValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView2);
tvUsageReminderValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView3);
tvCurrentBatteryValue = (TextView) rootView.findViewById(R.id.fragmentandroidviewTextView4);
// 进度条控件绑定
// 进度条控件绑定(自定义 VerticalSeekBar
sbChargeReminder = (VerticalSeekBar) rootView.findViewById(R.id.fragmentandroidviewVerticalSeekBar1);
sbUsageReminder = (VerticalSeekBar) rootView.findViewById(R.id.fragmentandroidviewVerticalSeekBar2);
// 图标控件绑定
@@ -132,49 +174,159 @@ public class MainContentView {
}
/**
* 绑定视图事件监听Java7 显式实现接口,避免 Lambda,适配 API30 事件分发
* 初始化配置变更确认对话框(单例复用,无模式,点击外部关闭,适配 API30 对话框机制
*/
private void initConfirmDialog() {
LogUtils.d(TAG, "initConfirmDialog: 开始初始化确认对话框");
if (mContext == null) {
LogUtils.e(TAG, "initConfirmDialog: Context 为空,初始化失败");
return;
}
// 构建无模式对话框(点击外部可关闭,取消改动)
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("配置变更确认");
builder.setMessage("是否确认修改当前配置?");
// 确定按钮:保存配置+回调+更新视图
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
confirmConfigChange(); // 确认变更,保存配置
dialog.dismiss();
}
});
// 取消按钮:恢复原始配置(补充物理取消按钮,提升用户体验)
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelConfigChange(); // 取消变更,恢复原始值
dialog.dismiss();
}
});
// 对话框外部点击监听:关闭对话框+恢复原始配置
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelConfigChange(); // 取消变更,恢复原始值
dialog.dismiss();
}
});
// 初始化对话框实例(设置可取消,支持外部点击关闭)
mConfigConfirmDialog = builder.create();
mConfigConfirmDialog.setCancelable(true);
mConfigConfirmDialog.setCanceledOnTouchOutside(true);
LogUtils.d(TAG, "initConfirmDialog: 确认对话框初始化完成");
}
/**
* 绑定视图事件监听Java7 显式实现接口,适配 API30 事件分发,修复进度条弹窗失效)
*/
private void bindViewListeners() {
LogUtils.d(TAG, "bindViewListeners: 开始绑定事件监听");
// 依赖校验,避免空指针
if (mAppConfigUtils == null || mActionListener == null) {
if (mAppConfigUtils == null || mActionListener == null || mConfigConfirmDialog == null) {
LogUtils.e(TAG, "bindViewListeners: 依赖实例为空,跳过监听绑定");
return;
}
// 充电提醒进度条监听
// 充电提醒进度条监听(使用 VerticalSeekBar 专属接口确保弹窗100%触发)
if (sbChargeReminder != null) {
sbChargeReminder.setOnSeekBarChangeListener(new ChargeReminderSeekBarListener());
LogUtils.d(TAG, "bindViewListeners: 充电提醒进度条监听绑定完成");
sbChargeReminder.setOnVerticalSeekBarTouchListener(new VerticalSeekBar.OnVerticalSeekBarTouchListener() {
@Override
public void onTouchUp(VerticalSeekBar seekBar, int progress) {
int originalValue = mAppConfigUtils.getChargeReminderValue();
// 进度无变化,不处理
if (originalValue == progress) {
LogUtils.d(TAG, "ChargeReminderSeekBar: 进度无变化,跳过");
return;
}
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_CHARGE_SEEKBAR, originalValue, progress);
showConfigConfirmDialog();
LogUtils.d(TAG, "ChargeReminderSeekBar: 触摸抬起触发变更,原始值=" + originalValue + ", 新进度=" + progress);
}
@Override
public void onTouchCancel(VerticalSeekBar seekBar, int progress) {
// 触摸取消回滚视图进度UI 与配置保持一致)
int originalValue = mAppConfigUtils.getChargeReminderValue();
if (tvChargeReminderValue != null && mChargeReminderBatteryDrawable != null && ivChargeReminderBattery != null) {
mChargeReminderBatteryDrawable.setBatteryValue(originalValue);
ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable);
tvChargeReminderValue.setText(originalValue + "%");
}
seekBar.setProgress(originalValue);
LogUtils.d(TAG, "ChargeReminderSeekBar: 触摸取消,进度回滚至=" + originalValue);
}
});
LogUtils.d(TAG, "bindViewListeners: 充电提醒进度条专属监听绑定完成");
}
// 充电提醒开关监听
if (cbEnableChargeReminder != null) {
cbEnableChargeReminder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean isChecked = cbEnableChargeReminder.isChecked();
mAppConfigUtils.setChargeReminderEnabled((Activity) mContext, isChecked);
mActionListener.onChargeReminderSwitchChanged(isChecked);
LogUtils.d(TAG, "cbEnableChargeReminder: 状态切换为 " + isChecked);
boolean originalValue = mAppConfigUtils.isChargeReminderEnabled();
boolean newValue = cbEnableChargeReminder.isChecked();
// 状态无变化,不处理
if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_CHARGE_SWITCH, originalValue, newValue);
showConfigConfirmDialog();
LogUtils.d(TAG, "cbEnableChargeReminder: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
}
});
LogUtils.d(TAG, "bindViewListeners: 充电提醒开关监听绑定完成");
}
// 耗电提醒进度条监听
// 耗电提醒进度条监听(使用 VerticalSeekBar 专属接口确保弹窗100%触发)
if (sbUsageReminder != null) {
sbUsageReminder.setOnSeekBarChangeListener(new UsageReminderSeekBarListener());
LogUtils.d(TAG, "bindViewListeners: 耗电提醒进度条监听绑定完成");
sbUsageReminder.setOnVerticalSeekBarTouchListener(new VerticalSeekBar.OnVerticalSeekBarTouchListener() {
@Override
public void onTouchUp(VerticalSeekBar seekBar, int progress) {
int originalValue = mAppConfigUtils.getUsageReminderValue();
// 进度无变化,不处理
if (originalValue == progress) {
LogUtils.d(TAG, "UsageReminderSeekBar: 进度无变化,跳过");
return;
}
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_USAGE_SEEKBAR, originalValue, progress);
showConfigConfirmDialog();
LogUtils.d(TAG, "UsageReminderSeekBar: 触摸抬起触发变更,原始值=" + originalValue + ", 新进度=" + progress);
}
@Override
public void onTouchCancel(VerticalSeekBar seekBar, int progress) {
// 触摸取消回滚视图进度UI 与配置保持一致)
int originalValue = mAppConfigUtils.getUsageReminderValue();
if (tvUsageReminderValue != null && mUsageReminderBatteryDrawable != null && ivUsageReminderBattery != null) {
mUsageReminderBatteryDrawable.setBatteryValue(originalValue);
ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable);
tvUsageReminderValue.setText(originalValue + "%");
}
seekBar.setProgress(originalValue);
LogUtils.d(TAG, "UsageReminderSeekBar: 触摸取消,进度回滚至=" + originalValue);
}
});
LogUtils.d(TAG, "bindViewListeners: 耗电提醒进度条专属监听绑定完成");
}
// 耗电提醒开关监听
if (cbEnableUsageReminder != null) {
cbEnableUsageReminder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean isChecked = cbEnableUsageReminder.isChecked();
mAppConfigUtils.setUsageReminderEnabled((Activity) mContext, isChecked);
mActionListener.onUsageReminderSwitchChanged(isChecked);
LogUtils.d(TAG, "cbEnableUsageReminder: 状态切换为 " + isChecked);
boolean originalValue = mAppConfigUtils.isUsageReminderEnabled();
boolean newValue = cbEnableUsageReminder.isChecked();
// 状态无变化,不处理
if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_USAGE_SWITCH, originalValue, newValue);
showConfigConfirmDialog();
LogUtils.d(TAG, "cbEnableUsageReminder: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
}
});
LogUtils.d(TAG, "bindViewListeners: 耗电提醒开关监听绑定完成");
@@ -185,10 +337,14 @@ public class MainContentView {
swEnableService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean isChecked = ((Switch) v).isChecked();
mAppConfigUtils.setServiceEnabled((Activity) mContext, isChecked);
mActionListener.onServiceSwitchChanged(isChecked);
LogUtils.d(TAG, "swEnableService: 状态切换为 " + isChecked);
boolean originalValue = mAppConfigUtils.isServiceEnabled();
boolean newValue = ((Switch) v).isChecked();
// 状态无变化,不处理
if (originalValue == newValue) return;
// 缓存变更数据,显示确认对话框
mTempConfigData = new TempConfigData(CHANGE_TYPE_SERVICE_SWITCH, originalValue, newValue);
showConfigConfirmDialog();
LogUtils.d(TAG, "swEnableService: 触发变更,原始值=" + originalValue + ", 变更后=" + newValue);
}
});
LogUtils.d(TAG, "bindViewListeners: 服务总开关监听绑定完成");
@@ -300,6 +456,18 @@ public class MainContentView {
*/
public void releaseResources() {
LogUtils.d(TAG, "releaseResources: 开始释放资源");
// 释放对话框资源(安全销毁,避免内存泄漏)
if (mConfigConfirmDialog != null) {
if (mConfigConfirmDialog.isShowing()) {
mConfigConfirmDialog.dismiss();
}
mConfigConfirmDialog.setOnDismissListener(null);
mConfigConfirmDialog.setOnCancelListener(null);
mConfigConfirmDialog = null;
}
// 释放临时数据
mTempConfigData = null;
// 释放 BatteryDrawable 资源(重点回收绘制资源,避免 OOM
mCurrentBatteryDrawable = null;
mChargeReminderBatteryDrawable = null;
@@ -331,83 +499,178 @@ public class MainContentView {
LogUtils.d(TAG, "releaseResources: 所有资源释放完成");
}
// ======================== 内部核心逻辑方法(对话框相关,封装确认/取消逻辑)========================
/**
* 显示配置变更确认对话框(确保 Activity 处于前台,避免异常,防止重复弹窗)
*/
private void showConfigConfirmDialog() {
// 对话框状态锁:正在显示则跳过,避免重复触发
if (isDialogShowing) {
LogUtils.d(TAG, "showConfigConfirmDialog: 对话框已显示,跳过重复调用");
return;
}
// 基础校验:对话框/上下文为空
if (mConfigConfirmDialog == null || mContext == null) {
LogUtils.e(TAG, "showConfigConfirmDialog: 对话框/上下文异常,无法显示");
if (mTempConfigData != null) cancelConfigChange();
return;
}
// Activity 状态校验:避免销毁后弹窗崩溃(适配 API30
Activity activity = (Activity) mContext;
if (activity.isFinishing() || activity.isDestroyed()) {
LogUtils.e(TAG, "showConfigConfirmDialog: Activity 已销毁,无法显示对话框");
if (mTempConfigData != null) cancelConfigChange();
return;
}
// 显示对话框,设置状态锁+关闭监听
if (!mConfigConfirmDialog.isShowing()) {
isDialogShowing = true;
mConfigConfirmDialog.show();
// 对话框关闭时解锁(无论确认/取消/外部点击,均解锁)
mConfigConfirmDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
isDialogShowing = false;
mConfigConfirmDialog.setOnDismissListener(null); // 移除监听,避免内存泄漏
}
});
LogUtils.d(TAG, "showConfigConfirmDialog: 确认对话框显示成功");
}
}
/**
* 确认配置变更(保存数据+回调监听+更新视图)
*/
private void confirmConfigChange() {
if (mTempConfigData == null || mAppConfigUtils == null || mActionListener == null) {
LogUtils.e(TAG, "confirmConfigChange: 依赖数据为空,确认失败");
return;
}
switch (mTempConfigData.changeType) {
// 充电提醒开关(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_CHARGE_SWITCH:
mAppConfigUtils.setChargeReminderEnabled(mTempConfigData.newBooleanValue);
mActionListener.onChargeReminderSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 充电提醒开关确认,值=" + mTempConfigData.newBooleanValue);
break;
// 耗电提醒开关(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_USAGE_SWITCH:
mAppConfigUtils.setUsageReminderEnabled(mTempConfigData.newBooleanValue);
mActionListener.onUsageReminderSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 耗电提醒开关确认,值=" + mTempConfigData.newBooleanValue);
break;
// 服务总开关(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_SERVICE_SWITCH:
mAppConfigUtils.setServiceEnabled(mTempConfigData.newBooleanValue);
mActionListener.onServiceSwitchChanged(mTempConfigData.newBooleanValue);
LogUtils.d(TAG, "confirmConfigChange: 服务开关确认,值=" + mTempConfigData.newBooleanValue);
break;
// 充电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_CHARGE_SEEKBAR:
mAppConfigUtils.setChargeReminderValue(mTempConfigData.newIntValue);
mActionListener.onChargeReminderProgressChanged(mTempConfigData.newIntValue);
LogUtils.d(TAG, "confirmConfigChange: 充电提醒进度确认,值=" + mTempConfigData.newIntValue);
break;
// 耗电提醒进度条(适配 AppConfigUtils 无 Activity 参数调用)
case CHANGE_TYPE_USAGE_SEEKBAR:
mAppConfigUtils.setUsageReminderValue(mTempConfigData.newIntValue);
mActionListener.onUsageReminderProgressChanged(mTempConfigData.newIntValue);
LogUtils.d(TAG, "confirmConfigChange: 耗电提醒进度确认,值=" + mTempConfigData.newIntValue);
break;
default:
LogUtils.w(TAG, "confirmConfigChange: 未知变更类型,跳过");
break;
}
// 确认完成,清空临时数据
mTempConfigData = null;
}
/**
* 取消配置变更(恢复原始值+刷新视图,确保 UI 与配置一致)
*/
private void cancelConfigChange() {
if (mTempConfigData == null || mAppConfigUtils == null) {
LogUtils.e(TAG, "cancelConfigChange: 依赖数据为空,取消失败");
return;
}
switch (mTempConfigData.changeType) {
// 充电提醒开关:恢复原始状态
case CHANGE_TYPE_CHARGE_SWITCH:
if (cbEnableChargeReminder != null) {
cbEnableChargeReminder.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 充电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 耗电提醒开关:恢复原始状态
case CHANGE_TYPE_USAGE_SWITCH:
if (cbEnableUsageReminder != null) {
cbEnableUsageReminder.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 耗电提醒开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 服务总开关:恢复原始状态
case CHANGE_TYPE_SERVICE_SWITCH:
if (swEnableService != null) {
swEnableService.setChecked(mTempConfigData.originalBooleanValue);
}
LogUtils.d(TAG, "cancelConfigChange: 服务开关取消,恢复值=" + mTempConfigData.originalBooleanValue);
break;
// 充电提醒进度条:恢复原始进度+更新视图
case CHANGE_TYPE_CHARGE_SEEKBAR:
if (sbChargeReminder != null) {
sbChargeReminder.setProgress(mTempConfigData.originalIntValue);
}
if (tvChargeReminderValue != null && mChargeReminderBatteryDrawable != null && ivChargeReminderBattery != null) {
mChargeReminderBatteryDrawable.setBatteryValue(mTempConfigData.originalIntValue);
ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable);
tvChargeReminderValue.setText(mTempConfigData.originalIntValue + "%");
}
LogUtils.d(TAG, "cancelConfigChange: 充电提醒进度取消,恢复值=" + mTempConfigData.originalIntValue);
break;
// 耗电提醒进度条:恢复原始进度+更新视图
case CHANGE_TYPE_USAGE_SEEKBAR:
if (sbUsageReminder != null) {
sbUsageReminder.setProgress(mTempConfigData.originalIntValue);
}
if (tvUsageReminderValue != null && mUsageReminderBatteryDrawable != null && ivUsageReminderBattery != null) {
mUsageReminderBatteryDrawable.setBatteryValue(mTempConfigData.originalIntValue);
ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable);
tvUsageReminderValue.setText(mTempConfigData.originalIntValue + "%");
}
LogUtils.d(TAG, "cancelConfigChange: 耗电提醒进度取消,恢复值=" + mTempConfigData.originalIntValue);
break;
default:
LogUtils.w(TAG, "cancelConfigChange: 未知变更类型,跳过");
break;
}
// 取消完成,清空临时数据
mTempConfigData = null;
}
// ======================== 内部工具方法(封装重复逻辑,提升复用性)========================
/**
* 获取资源颜色(适配 API30 主题颜色读取机制,兼容低版本,优化小米机型颜色显示)
* 获取资源颜色(适配 API30 主题颜色读取机制,兼容低版本,优化小米机型颜色显示,防御空指针
* @param colorResId 颜色资源 ID
* @return 校准后的颜色值
*/
private int getResourceColor(int colorResId) {
// 空指针防御Context 为空返回默认黑色
if (mContext == null) {
LogUtils.e(TAG, "getResourceColor: Context 为空,返回默认黑色");
return 0xFF000000;
}
// 适配 API30 主题颜色读取
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// API23+ 支持主题颜色,适配 API30 主题机制
return mContext.getResources().getColor(colorResId, mContext.getTheme());
} else {
// 低版本兼容
return mContext.getResources().getColor(colorResId);
}
}
// ======================== 内部事件监听类(私有封装,职责单一,避免外部依赖)========================
/**
* 充电提醒进度条监听(仅处理充电提醒进度相关逻辑)
*/
private class ChargeReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// 实时更新视图(联动 BatteryDrawable适配 API30 实时渲染)
if (tvChargeReminderValue != null && mChargeReminderBatteryDrawable != null && ivChargeReminderBattery != null) {
mChargeReminderBatteryDrawable.setBatteryValue(progress);
ivChargeReminderBattery.setImageDrawable(mChargeReminderBatteryDrawable);
tvChargeReminderValue.setText(progress + "%");
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 触摸开始,无额外逻辑,留空保持接口完整
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 触摸结束,保存配置并回调
if (mAppConfigUtils == null || mActionListener == null) return;
int progress = seekBar.getProgress();
mAppConfigUtils.setChargeReminderValue((Activity) mContext, progress);
mActionListener.onChargeReminderProgressChanged(progress);
LogUtils.d(TAG, "ChargeReminderSeekBar: 进度确认,保存值=" + progress);
}
}
/**
* 耗电提醒进度条监听(仅处理耗电提醒进度相关逻辑)
*/
private class UsageReminderSeekBarListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// 实时更新视图(联动 BatteryDrawable适配 API30 实时渲染)
if (tvUsageReminderValue != null && mUsageReminderBatteryDrawable != null && ivUsageReminderBattery != null) {
mUsageReminderBatteryDrawable.setBatteryValue(progress);
ivUsageReminderBattery.setImageDrawable(mUsageReminderBatteryDrawable);
tvUsageReminderValue.setText(progress + "%");
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 触摸开始,无额外逻辑,留空保持接口完整
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 触摸结束,保存配置并回调
if (mAppConfigUtils == null || mActionListener == null) return;
int progress = seekBar.getProgress();
mAppConfigUtils.setUsageReminderValue((Activity) mContext, progress);
mActionListener.onUsageReminderProgressChanged(progress);
LogUtils.d(TAG, "UsageReminderSeekBar: 进度确认,保存值=" + progress);
}
}
// ======================== 事件回调接口(解耦视图与业务,提升扩展性)========================
public interface OnViewActionListener {
void onChargeReminderSwitchChanged(boolean isChecked);

View File

@@ -11,16 +11,17 @@ import cc.winboll.studio.libappbase.LogUtils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 14:11
* @Describe 垂直进度条控件,适配 API30支持逆时针旋转0在下100在上修复滑块同步bug
* @Describe 垂直进度条控件,适配 API30支持逆时针旋转0在下100在上修复滑块同步+弹窗触发bug
*/
public class VerticalSeekBar extends SeekBar {
// ======================== 静态常量(置顶,唯一标识)========================
// ======================== 静态常量 =========================
private static final String TAG = VerticalSeekBar.class.getSimpleName();
// ======================== 成员变量(私有优先, volatile 关键字保留,确保线程可见性)========================
// ======================== 成员变量 =========================
private volatile int mProgress = -1; // 当前进度缓存,修复滑块同步问题
private OnVerticalSeekBarTouchListener mTouchListener; // 触摸事件回调接口(核心新增)
// ======================== 构造方法(按参数个数升序排列,适配 Java7 语法)========================
// ======================== 构造方法 =========================
public VerticalSeekBar(Context context) {
super(context);
initView();
@@ -39,86 +40,106 @@ public class VerticalSeekBar extends SeekBar {
LogUtils.d(TAG, "VerticalSeekBar: 三参数构造方法初始化完成");
}
// ======================== 初始化方法(封装通用逻辑,避免构造方法冗余)========================
/**
* 初始化视图配置,适配 API30 资源管控
*/
// ======================== 初始化方法 =========================
private void initView() {
// 移除水平默认阴影,优化垂直显示效果,减少 API30 不必要的绘制开销
setBackgroundDrawable(null);
LogUtils.d(TAG, "initView: 视图初始化完成,移除默认背景阴影");
}
// ======================== 重写测量/布局方法(按执行顺序排列:测量→尺寸变化→绘制========================
// ======================== 核心接口(新增,用于弹窗触发回调========================
/**
* 重写测量方法,交换宽高适配垂直显示,兼容 API30 测量机制
* 垂直进度条触摸事件回调接口,解决原生 OnSeekBarChangeListener 回调失效问题
* 直接在触摸抬起时回调确保配置变更对话框100%触发
*/
public interface OnVerticalSeekBarTouchListener {
/**
* 触摸抬起时回调(滑块停止滑动,触发弹窗的核心时机)
* @param seekBar 当前垂直进度条实例
* @param progress 最终滑动进度0~100
*/
void onTouchUp(VerticalSeekBar seekBar, int progress);
/**
* 触摸取消时回调(可选,用于异常场景进度回滚)
* @param seekBar 当前垂直进度条实例
* @param progress 取消时的进度
*/
void onTouchCancel(VerticalSeekBar seekBar, int progress);
}
/**
* 设置触摸事件监听器(给外部调用,如 MainContentView 绑定)
* @param listener 触摸事件回调实例
*/
public void setOnVerticalSeekBarTouchListener(OnVerticalSeekBarTouchListener listener) {
this.mTouchListener = listener;
LogUtils.d(TAG, "setOnVerticalSeekBarTouchListener: 触摸监听器绑定完成");
}
// ======================== 重写测量/布局/绘制方法 =========================
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
// 交换测量结果,将原高度作为宽度、原宽度作为高度,实现垂直布局
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
LogUtils.v(TAG, "onMeasure: 垂直测量完成,宽=" + getMeasuredHeight() + ", 高=" + getMeasuredWidth());
}
/**
* 重写尺寸变化方法,确保进度变化时视图同步刷新,适配 API30 布局刷新机制
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
LogUtils.v(TAG, "onSizeChanged: 尺寸变化,新宽=" + h + ", 新高=" + w + ", 旧宽=" + oldh + ", 旧高=" + oldw);
LogUtils.v(TAG, "onSizeChanged: 尺寸变化,新宽=" + h + ", 新高=" + w);
}
/**
* 重写绘制方法逆时针旋转90度实现垂直显示0在下100在上适配 API30 画布渲染
*/
@Override
protected void onDraw(Canvas canvas) {
// 逆时针旋转90度平移画布避免绘制偏移核心垂直显示逻辑
// 逆时针旋转90度平移画布避免绘制偏移0在下100在上
canvas.rotate(-90);
canvas.translate(-getHeight(), 0);
super.onDraw(canvas);
LogUtils.v(TAG, "onDraw: 垂直绘制完成,旋转角度=-90°");
}
// ======================== 重写触摸事件方法(核心交互逻辑,适配 API30 事件分发========================
/**
* 重写触摸事件,转换坐标计算垂直进度,确保 OnSeekBarChangeListener 正常回调
*/
// ======================== 重写触摸事件(优化事件透传,新增接口回调========================
@Override
public boolean onTouchEvent(MotionEvent event) {
// 先调用父类方法,保证 OnSeekBarChangeListener 的 onStart/onStopTrackingTouch 正常触发(关键!)
boolean handled = super.onTouchEvent(event);
LogUtils.d(TAG, "onTouchEvent: 触摸事件触发action=" + event.getAction() + ", 父类处理结果=" + handled);
// 先调用父类方法,保留原生监听器兼容性,同时强制透传事件
super.onTouchEvent(event);
boolean handled = true; // 强制消费事件,避免事件被拦截导致回调丢失
if (handled) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
LogUtils.d(TAG, "onTouchEvent: 触摸按下坐标Y=" + event.getY());
break;
case MotionEvent.ACTION_MOVE:
// 计算垂直进度逆时针旋转Y越小进度越大0在下100在上
calculateProgress(event.getY());
setProgress(mProgress);
LogUtils.v(TAG, "onTouchEvent: 触摸滑动,进度更新为=" + mProgress);
break;
case MotionEvent.ACTION_UP:
// 滑动结束,最终更新进度
calculateProgress(event.getY());
setProgress(mProgress);
LogUtils.d(TAG, "onTouchEvent: 触摸抬起,最终进度=" + mProgress);
break;
case MotionEvent.ACTION_CANCEL:
LogUtils.d(TAG, "onTouchEvent: 触摸取消,进度保持=" + getProgress());
break;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
LogUtils.d(TAG, "onTouchEvent: 触摸按下坐标Y=" + event.getY());
break;
case MotionEvent.ACTION_MOVE:
calculateProgress(event.getY());
setProgress(mProgress);
LogUtils.v(TAG, "onTouchEvent: 触摸滑动,进度更新为=" + mProgress);
break;
case MotionEvent.ACTION_UP:
calculateProgress(event.getY());
setProgress(mProgress);
LogUtils.d(TAG, "onTouchEvent: 触摸抬起,触发弹窗回调,进度=" + mProgress);
// 核心:调用新增接口,直接通知外部触发配置变更对话框
if (mTouchListener != null) {
mTouchListener.onTouchUp(this, mProgress);
}
break;
case MotionEvent.ACTION_CANCEL:
LogUtils.d(TAG, "onTouchEvent: 触摸取消,进度保持=" + getProgress());
// 可选:触摸取消时回调,外部可做进度回滚处理
if (mTouchListener != null) {
mTouchListener.onTouchCancel(this, getProgress());
}
break;
}
// 返回父类处理结果,确保事件分发完整,适配 API30 事件机制
return handled;
}
// ======================== 重写进度设置方法修复滑块同步bug适配 API30 进度更新)========================
// ======================== 重写进度设置方法 =========================
/**
* 重写进度设置,调用尺寸变化方法强制刷新,解决 setProgress 滑块不跟随问题
*/
@@ -131,7 +152,7 @@ public class VerticalSeekBar extends SeekBar {
LogUtils.d(TAG, "setProgress: 进度设置完成,进度=" + progress + ", 滑块同步刷新");
}
// ======================== 内部工具方法(封装重复逻辑,提升复用性)========================
// ======================== 内部工具方法 =========================
/**
* 计算垂直进度,校准范围 0~100避免异常值
* @param touchY 触摸点Y坐标