From 1e9a6adc88afddf747b126abaca03089e9d00920 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 17 Dec 2025 14:50:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=B5=E9=87=8F=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E6=8B=89=E5=8A=A8=E6=97=B6=E6=97=A0=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../powerbell/utils/AppConfigUtils.java | 191 ++------ .../powerbell/views/MainContentView.java | 443 ++++++++++++++---- .../powerbell/views/VerticalSeekBar.java | 121 +++-- 3 files changed, 475 insertions(+), 280 deletions(-) diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java index cd85613..fcba7e1 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/AppConfigUtils.java @@ -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: 所有配置保存完成"); } /** diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java index 8d79393..ec7291c 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/MainContentView.java @@ -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&豆包大模型 * @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); diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/VerticalSeekBar.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/VerticalSeekBar.java index 91ff70e..b4eae36 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/views/VerticalSeekBar.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/views/VerticalSeekBar.java @@ -11,16 +11,17 @@ import cc.winboll.studio.libappbase.LogUtils; /** * @Author ZhanGSKen&豆包大模型 * @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坐标