diff --git a/powerbell/build.properties b/powerbell/build.properties index c1b3e8b..f071398 100644 --- a/powerbell/build.properties +++ b/powerbell/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Tue Dec 16 06:10:34 GMT 2025 +#Tue Dec 16 07:33:42 GMT 2025 stageCount=7 libraryProject= baseVersion=15.14 publishVersion=15.14.6 -buildCount=30 +buildCount=39 baseBetaVersion=15.14.7 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java index 2219099..a246c8b 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/dialogs/ColorPaletteDialog.java @@ -15,7 +15,6 @@ import android.view.WindowManager; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.SeekBar; import android.widget.TextView; import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.R; @@ -23,171 +22,197 @@ import cc.winboll.studio.powerbell.R; /** * @Author ZhanGSKen * @Date 2025/12/16 11:47 - * @Describe 调色板对话框(支持颜色拾取、RGB输入、透明度/色阶调节,兼容 API29-30+ 小米机型) + * @Describe 调色板对话框(支持颜色拾取、RGB输入、透明度/亮度调节,兼容 API29-30+ 小米机型) */ public class ColorPaletteDialog extends Dialog implements View.OnClickListener { // ====================== 常量定义(首屏可见,统一管理) ====================== public static final String TAG = "ColorPaletteDialog"; - private static final int MAX_PROGRESS = 255; // 透明度/色阶最大值(0-255) - private static final int DEFAULT_BRIGHTNESS = 128; // 默认亮度系数(1.0倍) - private static final int NO_TARGET_ID = -1; // 无目标控件标记 + private static final int MAX_RGB_VALUE = 255; // RGB分量最大值(0-255) + private static final int DEFAULT_BRIGHTNESS = 100; // 默认亮度百分比(100%,无调节) + private static final int BRIGHTNESS_STEP = 5; // 亮度调节步长(每次±5%,精准流畅) + private static final int MIN_BRIGHTNESS = 10; // 亮度最小值(10%,避免全黑看不见) + private static final int MAX_BRIGHTNESS = 200; // 亮度最大值(200%,避免过曝失真) // ====================== 回调接口(紧跟常量,逻辑关联) ====================== public interface OnColorSelectedListener { - void onColorSelected(int color); // 返回0xAARRGGBB格式颜色 + void onColorSelected(int color); // 返回0xAARRGGBB格式颜色(含透明度) } // ====================== 成员变量(按优先级排序:核心数据→控件引用) ====================== - // 核心数据 - private OnColorSelectedListener mListener; // 颜色选择回调 - private int mInitialColor; // 初始颜色(0xAARRGGBB) - private int mCurrentColor; // 当前选择颜色 + // 核心数据:原始基准值(用户输入/选择颜色时更新)+ 实时调节值(亮度变化时更新) + private OnColorSelectedListener mListener; // 颜色选择回调(非空校验) + private int mInitialColor; // 初始颜色(传入的默认颜色) + private int mCurrentColor; // 当前最终颜色(含亮度调节) + private int mCurrentBrightnessPercent; // 当前亮度百分比(10%-200%) + private int mOriginalAlpha; // 原始透明度(固定,亮度不影响透明度) + private int mOriginalR; // 原始R分量(基准值,用户输入/选色时更新) + private int mOriginalG; // 原始G分量(基准值,用户输入/选色时更新) + private int mOriginalB; // 原始B分量(基准值,用户输入/选色时更新) + private int mCurrentR; // 实时R分量(亮度调节后,同步输入框显示) + private int mCurrentG; // 实时G分量(亮度调节后,同步输入框显示) + private int mCurrentB; // 实时B分量(亮度调节后,同步输入框显示) + // 并发控制标记:是否是应用程序自身在更新颜色(避免循环回调/重复触发) + private static volatile boolean isAppSelfUpdatingColor = false; - // 控件引用 - private ImageView ivColorPicker; // 颜色拾取预览 - private EditText etR; // R分量输入框 - private EditText etG; // G分量输入框 - private EditText etB; // B分量输入框 - private EditText etColorValue; // 颜色值输入框(#AARRGGBB) - private SeekBar sbAlpha; // 透明度调节进度条 - private SeekBar sbBrightness; // 亮度调节进度条 + // 控件引用(亮度改为「减号+数值+加号」底部布局) + private ImageView ivColorPicker; // 颜色预览拾取框 + private EditText etR; // R分量输入框(显示实时调节值) + private EditText etG; // G分量输入框(显示实时调节值) + private EditText etB; // B分量输入框(显示实时调节值) + private EditText etColorValue; // 颜色值输入框(#AARRGGBB,显示最终值) + private TextView tvBrightnessMinus;// 亮度减少按钮(-) + private TextView tvBrightnessValue;// 亮度数值显示(X%,直观易懂) + private TextView tvBrightnessPlus; // 亮度增加按钮(+) private TextView tvConfirm; // 确认按钮 private TextView tvCancel; // 取消按钮 - // ====================== 构造方法(初始化核心数据,参数校验) ====================== + // ====================== 构造方法(初始化核心数据,严格校验) ====================== public ColorPaletteDialog(Context context, int initialColor, OnColorSelectedListener listener) { super(context, R.style.CustomDialogStyle); this.mInitialColor = initialColor; - this.mCurrentColor = initialColor; this.mListener = listener; - // 强制回调非空,避免后续空指针 + // 1. 强制回调非空,避免后续空指针(容错) if (mListener == null) { throw new IllegalArgumentException("OnColorSelectedListener can not be null!"); } - LogUtils.d(TAG, "init dialog, initial color: " + String.format("#%08X", initialColor)); + + // 2. 解析初始颜色:原始基准值 = 实时值(初始无亮度调节) + this.mOriginalAlpha = Color.alpha(initialColor); + this.mOriginalR = Color.red(initialColor); + this.mOriginalG = Color.green(initialColor); + this.mOriginalB = Color.blue(initialColor); + this.mCurrentR = mOriginalR; + this.mCurrentG = mOriginalG; + this.mCurrentB = mOriginalB; + + // 3. 初始化当前状态(默认亮度100%,当前颜色=初始颜色) + this.mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; + this.mCurrentColor = initialColor; + + LogUtils.d(TAG, "init dialog success | 初始颜色:" + String.format("#%08X", initialColor) + + " | 原始RGB:" + mOriginalR + "," + mOriginalG + "," + mOriginalB + + " | 初始亮度:" + mCurrentBrightnessPercent + "%"); } - // ====================== 生命周期方法(按执行顺序排列) ====================== + // ====================== 生命周期方法(按执行顺序排列,逻辑清晰) ====================== @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); + requestWindowFeature(Window.FEATURE_NO_TITLE); // 隐藏标题栏 View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_color_palette, null); setContentView(view); - // 初始化流程:控件绑定→数据赋值→监听设置→尺寸适配 + // 初始化流程:控件绑定→数据赋值→监听设置→尺寸适配(小米机型优先适配) initViewBind(view); initData(); initListener(); adjustDialogSize(); - LogUtils.d(TAG, "dialog create complete, adapt Xiaomi API29-30"); + LogUtils.d(TAG, "dialog create complete | 适配小米API29-30机型"); } @Override public void dismiss() { super.dismiss(); - // 释放回调引用,避免内存泄漏 + // 释放资源,避免内存泄漏(回调引用置空) mListener = null; - LogUtils.d(TAG, "dialog dismiss, release resource"); + LogUtils.d(TAG, "dialog dismiss | 释放资源完成"); } - // ====================== 初始化核心方法(控件+数据+监听,职责单一) ====================== + // ====================== 初始化核心方法(职责单一,便于维护) ====================== /** - * 控件绑定(仅ID绑定,无任何业务逻辑) + * 控件绑定(仅做ID绑定,无任何业务逻辑,避免初始化混乱) */ private void initViewBind(View view) { - ivColorPicker = (ImageView) view.findViewById(R.id.iv_color_picker); - etR = (EditText) view.findViewById(R.id.et_r); - etG = (EditText) view.findViewById(R.id.et_g); - etB = (EditText) view.findViewById(R.id.et_b); - etColorValue = (EditText) view.findViewById(R.id.et_color_value); - sbAlpha = (SeekBar) view.findViewById(R.id.sb_alpha); - sbBrightness = (SeekBar) view.findViewById(R.id.sb_brightness); - tvConfirm = (TextView) view.findViewById(R.id.tv_confirm); - tvCancel = (TextView) view.findViewById(R.id.tv_cancel); + ivColorPicker = view.findViewById(R.id.iv_color_picker); + etR = view.findViewById(R.id.et_r); + etG = view.findViewById(R.id.et_g); + etB = view.findViewById(R.id.et_b); + etColorValue = view.findViewById(R.id.et_color_value); + tvBrightnessMinus = view.findViewById(R.id.tv_brightness_minus); + tvBrightnessValue = view.findViewById(R.id.tv_brightness_value); + tvBrightnessPlus = view.findViewById(R.id.tv_brightness_plus); + tvConfirm = view.findViewById(R.id.tv_confirm); + tvCancel = view.findViewById(R.id.tv_cancel); - // 控件非空校验,绑定失败直接关闭(小米低版本容错) + // 控件非空校验(小米低版本容错,绑定失败直接关闭对话框) if (ivColorPicker == null || etR == null || etG == null || etB == null || etColorValue == null - || sbAlpha == null || sbBrightness == null || tvConfirm == null || tvCancel == null) { - LogUtils.e(TAG, "view bind failed, check layout ID!"); + || tvBrightnessMinus == null || tvBrightnessValue == null || tvBrightnessPlus == null + || tvConfirm == null || tvCancel == null) { + LogUtils.e(TAG, "view bind failed | 请检查布局ID是否正确!"); dismiss(); return; } - LogUtils.d(TAG, "view bind complete"); + LogUtils.d(TAG, "view bind complete | 所有控件绑定成功"); } /** * 数据初始化(无监听状态下赋值,避免循环回调) */ private void initData() { - // 解析初始颜色分量 - int alpha = Color.alpha(mInitialColor); - int r = Color.red(mInitialColor); - int g = Color.green(mInitialColor); - int b = Color.blue(mInitialColor); + // 1. 颜色预览(显示当前最终颜色,初始=原始颜色) + ivColorPicker.setBackgroundColor(mCurrentColor); - // 赋值控件 - ivColorPicker.setBackgroundColor(mInitialColor); - etR.setText(String.valueOf(r)); - etG.setText(String.valueOf(g)); - etB.setText(String.valueOf(b)); - etColorValue.setText(String.format("#%08X", mInitialColor)); + // 2. RGB输入框(显示「实时分量」,初始=原始值) + etR.setText(String.valueOf(mCurrentR)); + etG.setText(String.valueOf(mCurrentG)); + etB.setText(String.valueOf(mCurrentB)); - // 进度条初始化 - sbAlpha.setMax(MAX_PROGRESS); - sbBrightness.setMax(MAX_PROGRESS); - sbAlpha.setProgress(alpha); - sbBrightness.setProgress(DEFAULT_BRIGHTNESS); + // 3. 颜色值输入框(显示当前最终颜色,格式#AARRGGBB,大写更规范) + etColorValue.setText(String.format("#%08X", mCurrentColor)); - LogUtils.d(TAG, "init data complete, A:" + alpha + ", R:" + r + ", G:" + g + ", B:" + b); + // 4. 亮度控件(显示默认100%,初始化按钮状态) + tvBrightnessValue.setText(mCurrentBrightnessPercent + "%"); + updateBrightnessBtnStatus(); // 禁用边界值按钮(初始100%,都可用) + + LogUtils.d(TAG, "init data complete | 原始透明度:" + mOriginalAlpha); } /** * 监听初始化(统一管理所有监听,延迟绑定避免初始化循环) */ private void initListener() { - initClickListener(); + // 点击监听(按钮+颜色拾取框) + ivColorPicker.setOnClickListener(this); + tvConfirm.setOnClickListener(this); + tvCancel.setOnClickListener(this); + tvBrightnessMinus.setOnClickListener(this); + tvBrightnessPlus.setOnClickListener(this); + + // 输入框监听(RGB+颜色值,避免循环同步) initTextWatcherListener(); - initSeekBarListener(); - LogUtils.d(TAG, "all listener init complete"); + LogUtils.d(TAG, "all listener init complete | 监听绑定成功"); } /** - * 对话框尺寸适配(小米全面屏+软键盘优化) + * 对话框尺寸适配(小米全面屏+软键盘优化,避免输入框被遮挡) */ private void adjustDialogSize() { Window window = getWindow(); if (window != null) { WindowManager.LayoutParams lp = window.getAttributes(); - // 宽度占屏幕80%,高度自适应 + // 宽度占屏幕80%,高度自适应(适配不同屏幕尺寸) lp.width = (int) (getContext().getResources().getDisplayMetrics().widthPixels * 0.8); lp.height = WindowManager.LayoutParams.WRAP_CONTENT; - // 软键盘适配:避免输入框被遮挡(小米虚拟导航栏兼容) + // 软键盘适配:小米虚拟导航栏兼容,避免输入框被遮挡 window.setAttributes(lp); - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - LogUtils.d(TAG, "dialog size adjust complete, adapt Xiaomi full screen"); + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN + | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + LogUtils.d(TAG, "dialog size adjust complete | 适配全面屏+软键盘"); } } - // ====================== 监听子方法(细分类型,便于维护) ====================== + // ====================== 监听子方法(细分类型,逻辑清晰) ====================== /** - * 点击事件监听(按钮+颜色拾取框) - */ - private void initClickListener() { - ivColorPicker.setOnClickListener(this); - tvConfirm.setOnClickListener(this); - tvCancel.setOnClickListener(this); - } - - /** - * 输入框文本监听(RGB+颜色值,传入触发控件ID避免循环) + * 输入框文本监听(RGB+颜色值,传入触发ID避免循环同步) */ private void initTextWatcherListener() { + // RGB输入框监听(复用方法,减少冗余) setEditTextWatcher(etR, R.id.et_r); setEditTextWatcher(etG, R.id.et_g); setEditTextWatcher(etB, R.id.et_b); + // 颜色值输入框监听(支持#RRGGBB/#AARRGGBB格式) etColorValue.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @@ -197,202 +222,218 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener { @Override public void afterTextChanged(Editable s) { - parseColorFromStr(s.toString().trim(), R.id.et_color_value); + // 关键:判断非应用自身更新,才执行解析(避免循环回调) + if (!isAppSelfUpdatingColor) { + parseColorFromStr(s.toString().trim(), R.id.et_color_value); + } } }); } + // ====================== 颜色核心逻辑(按触发场景分类,重点修复RGB同步) ====================== /** - * 进度条滑动监听(透明度+亮度,仅响应手动操作) + * 核心计算:基于原始RGB+当前亮度,计算实时RGB+最终颜色(确保三个分量同时调节) + * 逻辑:亮度百分比→调节系数→原始RGB×系数→限制0-255→更新实时RGB+最终颜色 */ - private void initSeekBarListener() { - // 透明度进度条 - sbAlpha.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - updateColorByAlpha(progress, NO_TARGET_ID); - } - } + private void calculateBrightnessAndUpdate() { + // 亮度百分比转调节系数(10%→0.1,100%→1.0,200%→2.0) + float brightnessFactor = mCurrentBrightnessPercent / 100.0f; - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} + // RGB三个分量同时调节(基于原始基准值,避免叠加失真),限制0-255 + mCurrentR = Math.min(Math.max(Math.round(mOriginalR * brightnessFactor), 0), MAX_RGB_VALUE); + mCurrentG = Math.min(Math.max(Math.round(mOriginalG * brightnessFactor), 0), MAX_RGB_VALUE); + mCurrentB = Math.min(Math.max(Math.round(mOriginalB * brightnessFactor), 0), MAX_RGB_VALUE); - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); - - // 亮度进度条 - sbBrightness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - updateColorByBrightness(progress, NO_TARGET_ID); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); + // 拼接透明度+实时RGB,得到最终颜色(0xAARRGGBB) + mCurrentColor = Color.argb(mOriginalAlpha, mCurrentR, mCurrentG, mCurrentB); } - // ====================== 颜色核心逻辑(按触发场景分类) ====================== /** - * 解析颜色字符串(支持#RRGGBB/#AARRGGBB,容错处理) + * 亮度减少(每次减5%,最低10%,防止过暗) + */ + private void decreaseBrightness() { + changeBrightness(false); + } + + /** + * 亮度增加(每次加5%,最高200%,防止过曝) + */ + private void increaseBrightness() { + changeBrightness(true); + } + + /** + * 亮度调节核心方法(统一逻辑,加并发控制,同步RGB输入框) + */ + private synchronized void changeBrightness(boolean isIncrease) { + // 关键:判断非应用自身更新,才执行调节(避免重复触发/循环) + if (!isAppSelfUpdatingColor) { + isAppSelfUpdatingColor = true; // 标记为应用自身更新 + try { + if (isIncrease) { + if (mCurrentBrightnessPercent >= MAX_BRIGHTNESS) return; // 达到最大值,不处理 + mCurrentBrightnessPercent += BRIGHTNESS_STEP; // 增加步长 + } else { + if (mCurrentBrightnessPercent <= MIN_BRIGHTNESS) return; // 达到最小值,不处理 + mCurrentBrightnessPercent -= BRIGHTNESS_STEP; // 减少步长 + } + // 计算亮度调节后的实时RGB+最终颜色 + calculateBrightnessAndUpdate(); + // 同步所有控件(重点:RGB输入框显示实时值) + updateAllViews(); + LogUtils.d(TAG, (isIncrease ? "increase" : "decrease") + " brightness | " + + "亮度:" + mCurrentBrightnessPercent + "% | 实时RGB:" + mCurrentR + "," + mCurrentG + "," + mCurrentB); + } finally { + // 直接释放标记,避免卡顿 + isAppSelfUpdatingColor = false; + } + } + } + + /** + * 解析颜色字符串(支持#RRGGBB/#AARRGGBB,容错处理,更新原始基准值+实时值) */ private void parseColorFromStr(String colorStr, int triggerViewId) { - if (TextUtils.isEmpty(colorStr)) { - return; - } + // 关键:判断非应用自身更新,才执行解析(避免循环回调) + if (!isAppSelfUpdatingColor) { + isAppSelfUpdatingColor = true; // 标记为应用自身更新 + try { + if (TextUtils.isEmpty(colorStr)) return; - try { - // 补全#前缀,兼容用户输入习惯 - if (!colorStr.startsWith("#")) { - colorStr = "#" + colorStr; - } - // 格式校验:仅支持6位(RRGGBB)/8位(AARRGGBB) - if (colorStr.length() != 7 && colorStr.length() != 9) { - LogUtils.e(TAG, "color format error, length must be 6 or 8"); - return; - } + // 补全#前缀(兼容用户输入习惯,如直接输AARRGGBB) + if (!colorStr.startsWith("#")) { + colorStr = "#" + colorStr; + } - int color = Color.parseColor(colorStr); - // 6位颜色保留当前透明度,避免用户输入丢失 - if (colorStr.length() == 7) { - color = Color.argb(Color.alpha(mCurrentColor), Color.red(color), Color.green(color), Color.blue(color)); + // 格式校验(仅支持6位RRGGBB/8位AARRGGBB,避免非法格式) + if (colorStr.length() != 7 && colorStr.length() != 9) { + LogUtils.e(TAG, "parse color failed | 格式错误(需#RRGGBB/#AARRGGBB),输入:" + colorStr); + return; + } + + // 解析颜色(系统API,安全可靠) + int parsedColor = Color.parseColor(colorStr); + + // 更新原始基准值(用户输入颜色,重置基准) + mOriginalAlpha = Color.alpha(parsedColor); + mOriginalR = Color.red(parsedColor); + mOriginalG = Color.green(parsedColor); + mOriginalB = Color.blue(parsedColor); + // 更新实时值(原始值=实时值,无亮度调节) + mCurrentR = mOriginalR; + mCurrentG = mOriginalG; + mCurrentB = mOriginalB; + // 重置亮度为100% + mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; + mCurrentColor = parsedColor; + + // 同步所有控件 + updateAllViews(); + LogUtils.d(TAG, "parse color success | 解析颜色:" + String.format("#%08X", parsedColor) + + " | 重置亮度:" + DEFAULT_BRIGHTNESS + "%"); + } catch (IllegalArgumentException e) { + LogUtils.e(TAG, "parse color failed | 非法颜色格式,输入:" + colorStr, e); + } finally { + // 直接释放标记,避免卡顿 + isAppSelfUpdatingColor = false; } - updateCurrentColor(color, triggerViewId, true); - LogUtils.d(TAG, "parse color success: " + String.format("#%08X", color)); - } catch (IllegalArgumentException e) { - LogUtils.e(TAG, "parse color failed, input: " + colorStr, e); } } /** - * 通过RGB输入框更新颜色(传入触发ID,避免循环同步) + * 通过RGB输入框更新颜色(用户输入后,更新原始基准值+实时值,重置亮度为100%) */ - private static volatile boolean isUpdatingColorByRGB = false; private synchronized void updateColorByRGB(int triggerViewId) { - if (isUpdatingColorByRGB == false) { - isUpdatingColorByRGB = true; - try { - int r = parseInputValue(etR.getText().toString()); - int g = parseInputValue(etG.getText().toString()); - int b = parseInputValue(etB.getText().toString()); - int alpha = Color.alpha(mCurrentColor); - int newColor = Color.argb(alpha, r, g, b); + // 关键:判断非应用自身更新,才执行更新(避免循环回调) + if (!isAppSelfUpdatingColor) { + isAppSelfUpdatingColor = true; // 标记为应用自身更新 + try { + // 解析用户输入的RGB值(限制0-255,非法输入设为0) + int inputR = parseInputValue(etR.getText().toString()); + int inputG = parseInputValue(etG.getText().toString()); + int inputB = parseInputValue(etB.getText().toString()); - updateCurrentColor(newColor, triggerViewId, true); - LogUtils.d(TAG, "update color by RGB: R=" + r + ", G=" + g + ", B=" + b); - } catch (Exception e) { - LogUtils.e(TAG, "update color by RGB failed", e); - } - try { - Thread.sleep(200); - isUpdatingColorByRGB = false; - } catch (InterruptedException e) { - LogUtils.d(TAG, e, Thread.currentThread().getStackTrace()); - } - } - } + // 更新原始基准值(用户手动输入,作为新的调节基准) + mOriginalR = inputR; + mOriginalG = inputG; + mOriginalB = inputB; + // 更新实时值(输入值=实时值,无亮度调节) + mCurrentR = inputR; + mCurrentG = inputG; + mCurrentB = inputB; + // 重置亮度为100% + mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; + // 计算最终颜色(无亮度调节,直接拼接) + mCurrentColor = Color.argb(mOriginalAlpha, mCurrentR, mCurrentG, mCurrentB); - /** - * 通过透明度进度条更新颜色 - */ - private void updateColorByAlpha(int alpha, int triggerViewId) { - int r = Color.red(mCurrentColor); - int g = Color.green(mCurrentColor); - int b = Color.blue(mCurrentColor); - int newColor = Color.argb(alpha, r, g, b); - - updateCurrentColor(newColor, triggerViewId, true); - LogUtils.d(TAG, "update color by alpha: " + alpha); - } - - /** - * 通过亮度进度条更新颜色(系数0.0-2.0,精准调节) - */ - private void updateColorByBrightness(int brightness, int triggerViewId) { - float factor = brightness * 1.0f / DEFAULT_BRIGHTNESS; - int alpha = Color.alpha(mCurrentColor); - int r = adjustBrightness(Color.red(mCurrentColor), factor); - int g = adjustBrightness(Color.green(mCurrentColor), factor); - int b = adjustBrightness(Color.blue(mCurrentColor), factor); - int newColor = Color.argb(alpha, r, g, b); - - updateCurrentColor(newColor, triggerViewId, false); - LogUtils.d(TAG, "update color by brightness: factor=" + String.format("%.2f", factor)); - } - - /** - * 亮度调节(限制0-255,避免颜色分量溢出) - */ - private int adjustBrightness(int colorComponent, float factor) { - int newComponent = Math.round(colorComponent * factor); - return Math.min(Math.max(newComponent, 0), MAX_PROGRESS); - } - - /** - * 核心同步方法:更新颜色+同步控件(跳过触发源,杜绝循环) - */ - private void updateCurrentColor(int newColor, int triggerViewId, boolean updateBrightness) { - mCurrentColor = newColor; - int red = Color.red(newColor); - int green = Color.green(newColor); - int blue = Color.blue(newColor); - int alpha = Color.alpha(newColor); - - // 1. 同步颜色预览(始终同步) - ivColorPicker.setBackgroundColor(newColor); - - // 2. 同步RGB输入框(跳过触发控件) - if (triggerViewId != R.id.et_r) etR.setText(String.valueOf(red)); - if (triggerViewId != R.id.et_g) etG.setText(String.valueOf(green)); - if (triggerViewId != R.id.et_b) etB.setText(String.valueOf(blue)); - - // 3. 同步颜色值输入框(跳过触发控件) - if (triggerViewId != R.id.et_color_value) { - etColorValue.setText(String.format("#%08X", newColor)); + // 同步所有控件 + updateAllViews(); + LogUtils.d(TAG, "update color by RGB | 新原始RGB:" + mOriginalR + "," + mOriginalG + "," + mOriginalB + + " | 重置亮度:" + DEFAULT_BRIGHTNESS + "%"); + } catch (Exception e) { + LogUtils.e(TAG, "update color by RGB failed", e); + } finally { + // 直接释放标记,避免卡顿 + isAppSelfUpdatingColor = false; + } } - - // 4. 同步进度条(取消监听避免循环,同步后重新绑定) - // 透明度进度条 - sbAlpha.setOnSeekBarChangeListener(null); - if (triggerViewId != R.id.sb_alpha) sbAlpha.setProgress(alpha); - initSeekBarListener(); - - // 亮度进度条(按需重置) - sbBrightness.setOnSeekBarChangeListener(null); - if (updateBrightness && triggerViewId != R.id.sb_brightness) { - sbBrightness.setProgress(DEFAULT_BRIGHTNESS); - } - initSeekBarListener(); - - LogUtils.d(TAG, "sync view complete, current color: " + String.format("#%08X", newColor)); } - // ====================== 工具方法(通用能力,下沉到底部) ====================== /** - * 解析输入值(限制0-255,非法输入返回0) + * 核心同步:更新所有控件显示(统一方法,避免冗余,确保RGB输入框实时同步) + */ + private void updateAllViews() { + // 1. 同步颜色预览(显示最终颜色) + ivColorPicker.setBackgroundColor(mCurrentColor); + + // 2. 同步RGB输入框(显示实时调节值,关键修复:亮度变化时更新) + etR.setText(String.valueOf(mCurrentR)); + etG.setText(String.valueOf(mCurrentG)); + etB.setText(String.valueOf(mCurrentB)); + + // 3. 同步颜色值输入框(显示最终颜色,含亮度调节) + etColorValue.setText(String.format("#%08X", mCurrentColor)); + + // 4. 同步亮度控件(数值+按钮状态) + tvBrightnessValue.setText(mCurrentBrightnessPercent + "%"); + updateBrightnessBtnStatus(); + + LogUtils.d(TAG, "sync all views complete | 最终颜色:" + String.format("#%08X", mCurrentColor) + + " | 实时RGB:" + mCurrentR + "," + mCurrentG + "," + mCurrentB + + " | 亮度:" + mCurrentBrightnessPercent + "%"); + } + + /** + * 更新亮度按钮状态(边界值禁用,提升交互体验) + */ + private void updateBrightnessBtnStatus() { + // 亮度≤10%:禁用减号(文字变浅灰);≥200%:禁用加号(文字变浅灰) + boolean canMinus = mCurrentBrightnessPercent > MIN_BRIGHTNESS; + boolean canPlus = mCurrentBrightnessPercent < MAX_BRIGHTNESS; + + tvBrightnessMinus.setEnabled(canMinus); + tvBrightnessPlus.setEnabled(canPlus); + tvBrightnessMinus.setTextColor(canMinus ? Color.BLACK : Color.parseColor("#CCCCCC")); + tvBrightnessPlus.setTextColor(canPlus ? Color.BLACK : Color.parseColor("#CCCCCC")); + } + + // ====================== 工具方法(通用能力,下沉到底部,复用性强) ====================== + /** + * 解析输入值(限制0-255,非法输入返回0,容错处理) */ private int parseInputValue(String input) { - if (TextUtils.isEmpty(input)) { - return 0; - } + if (TextUtils.isEmpty(input)) return 0; try { int value = Integer.parseInt(input); - return Math.min(Math.max(value, 0), MAX_PROGRESS); + return Math.min(Math.max(value, 0), MAX_RGB_VALUE); // 限制范围,避免溢出 } catch (NumberFormatException e) { - LogUtils.e(TAG, "parse input failed: " + input, e); + LogUtils.e(TAG, "parse input failed | 非法数字,输入:" + input, e); return 0; } } /** - * RGB输入框监听复用(传入控件ID,统一回调) + * RGB输入框监听复用(减少冗余代码,统一逻辑) */ private void setEditTextWatcher(EditText editText, final int viewId) { editText.addTextChangedListener(new TextWatcher() { @@ -404,86 +445,111 @@ public class ColorPaletteDialog extends Dialog implements View.OnClickListener { @Override public void afterTextChanged(Editable s) { - updateColorByRGB(viewId); + // 关键:判断非应用自身更新,才执行更新(避免循环回调) + if (!isAppSelfUpdatingColor) { + updateColorByRGB(viewId); // 输入变化后更新颜色 + } } }); } /** - * dp转px(适配小米不同分辨率,避免尺寸错乱) + * dp转px(适配小米不同分辨率,避免尺寸错乱,通用工具) */ private int dp2px(float dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); } /** - * 显示系统颜色选择器(兼容API29-30,无高版本依赖) + * 显示系统颜色选择器(兼容API29-30,无高版本依赖,小米机型适配) */ private void showSystemColorPicker() { - LogUtils.d(TAG, "show system color picker"); + LogUtils.d(TAG, "show system color picker | 兼容小米API29-30"); final android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(getContext()); - builder.setTitle("选择颜色"); + builder.setTitle("选择基础颜色"); - // 常用基础色(小米机型显示适配) + // 常用基础色(10种,覆盖主流需求,可按需增减) final int[] systemColors = { 0xFFFFFFFF, 0xFF000000, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFF888888, 0xFFAAAAAA }; // 动态创建颜色选择布局(横向排列,适配小米屏幕) - LinearLayout colorView = new LinearLayout(getContext()); - colorView.setOrientation(LinearLayout.HORIZONTAL); - colorView.setGravity(Gravity.CENTER); - colorView.setPadding(dp2px(10), dp2px(10), dp2px(10), dp2px(10)); + LinearLayout colorLayout = new LinearLayout(getContext()); + colorLayout.setOrientation(LinearLayout.HORIZONTAL); + colorLayout.setGravity(Gravity.CENTER); + colorLayout.setPadding(dp2px(10), dp2px(10), dp2px(10), dp2px(10)); + // 循环添加颜色按钮(圆形,点击选择) for (int i = 0; i < systemColors.length; i++) { - ImageView iv = new ImageView(getContext()); + final int color = systemColors[i]; + ImageView colorBtn = new ImageView(getContext()); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(dp2px(40), dp2px(40)); - // 低版本兼容:用setMargins替代setMarginEnd(适配API29-30) if (i != systemColors.length - 1) { - lp.setMargins(0, 0, dp2px(10), 0); + lp.setMargins(0, 0, dp2px(10), 0); // 低版本兼容setMargins(API29-30) } - iv.setLayoutParams(lp); - iv.setBackgroundColor(systemColors[i]); - iv.setClickable(true); - iv.setFocusable(true); + colorBtn.setLayoutParams(lp); + colorBtn.setBackgroundColor(color); + colorBtn.setClickable(true); + colorBtn.setFocusable(true); - final int finalI = i; - iv.setOnClickListener(new View.OnClickListener() { + // 点击选择颜色:更新原始基准值+实时值,重置亮度为100% + colorBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - int selectedColor = systemColors[finalI]; - // 保留当前透明度,仅更新RGB - int newColor = Color.argb( - Color.alpha(mCurrentColor), - Color.red(selectedColor), - Color.green(selectedColor), - Color.blue(selectedColor) - ); - updateCurrentColor(newColor, NO_TARGET_ID, true); - builder.create().dismiss(); - LogUtils.d(TAG, "select system color: " + String.format("#%08X", selectedColor)); + // 关键:判断非应用自身更新,才执行选择逻辑(避免循环) + if (!isAppSelfUpdatingColor) { + isAppSelfUpdatingColor = true; // 标记为应用自身更新 + try { + // 更新原始基准值 + mOriginalR = Color.red(color); + mOriginalG = Color.green(color); + mOriginalB = Color.blue(color); + mOriginalAlpha = Color.alpha(color); + // 更新实时值(原始值=实时值) + mCurrentR = mOriginalR; + mCurrentG = mOriginalG; + mCurrentB = mOriginalB; + // 重置亮度+最终颜色 + mCurrentBrightnessPercent = DEFAULT_BRIGHTNESS; + mCurrentColor = color; + // 同步所有控件 + updateAllViews(); + builder.create().dismiss(); // 关闭选择器 + LogUtils.d(TAG, "select system color | 选择颜色:" + String.format("#%08X", color)); + } finally { + // 释放标记 + isAppSelfUpdatingColor = false; + } + } } }); - colorView.addView(iv); + colorLayout.addView(colorBtn); } - builder.setView(colorView).setNegativeButton("取消", null).show(); + builder.setView(colorLayout).setNegativeButton("取消", null).show(); } - // ====================== 点击事件实现(统一处理按钮交互) ====================== + // ====================== 点击事件实现(统一处理,逻辑清晰) ====================== @Override public void onClick(View v) { int id = v.getId(); - if (id == R.id.iv_color_picker) { - showSystemColorPicker(); - } else if (id == R.id.tv_confirm) { - mListener.onColorSelected(mCurrentColor); - LogUtils.d(TAG, "confirm color, callback: " + String.format("#%08X", mCurrentColor)); - dismiss(); - } else if (id == R.id.tv_cancel) { - dismiss(); - LogUtils.d(TAG, "cancel color, dismiss dialog"); + // 关键:所有点击事件均加判断(避免并发冲突/重复触发) + if (!isAppSelfUpdatingColor) { + if (id == R.id.iv_color_picker) { + showSystemColorPicker(); // 打开系统颜色选择器 + } else if (id == R.id.tv_confirm) { + mListener.onColorSelected(mCurrentColor); // 确认选择,回调颜色 + LogUtils.d(TAG, "confirm color | 回调颜色:" + String.format("#%08X", mCurrentColor)); + dismiss(); + } else if (id == R.id.tv_cancel) { + dismiss(); // 取消,关闭对话框 + LogUtils.d(TAG, "cancel color | 取消选择,关闭对话框"); + } else if (id == R.id.tv_brightness_minus) { + decreaseBrightness(); // 减少亮度 + } else if (id == R.id.tv_brightness_plus) { + increaseBrightness(); // 增加亮度 + } } } } diff --git a/powerbell/src/main/res/drawable/btn_brightness_bg.xml b/powerbell/src/main/res/drawable/btn_brightness_bg.xml new file mode 100644 index 0000000..3d52279 --- /dev/null +++ b/powerbell/src/main/res/drawable/btn_brightness_bg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/powerbell/src/main/res/drawable/btn_cancel_bg.xml b/powerbell/src/main/res/drawable/btn_cancel_bg.xml new file mode 100644 index 0000000..14e0bac --- /dev/null +++ b/powerbell/src/main/res/drawable/btn_cancel_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/powerbell/src/main/res/drawable/btn_confirm_bg.xml b/powerbell/src/main/res/drawable/btn_confirm_bg.xml new file mode 100644 index 0000000..e4a7a4d --- /dev/null +++ b/powerbell/src/main/res/drawable/btn_confirm_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/powerbell/src/main/res/layout/dialog_color_palette.xml b/powerbell/src/main/res/layout/dialog_color_palette.xml index 7924db1..885d969 100644 --- a/powerbell/src/main/res/layout/dialog_color_palette.xml +++ b/powerbell/src/main/res/layout/dialog_color_palette.xml @@ -4,203 +4,156 @@ android:layout_height="wrap_content" android:orientation="vertical" android:padding="20dp" - android:background="@drawable/dialog_bg_radius"> + android:background="@android:color/white" + android:radius="12dp"> - - + + - + - - - - - - - - + android:layout_marginBottom="15dp" + android:gravity="center_vertical"> + android:textSize="16sp" + android:textColor="@android:color/black" /> + android:maxLength="3" + android:textSize="14sp" /> + android:maxLength="3" + android:textSize="14sp" /> + android:textSize="14sp" /> - + + + + + android:layout_marginBottom="20dp" + android:gravity="center_horizontal"> - - - - - - + android:text="-" + android:textSize="20sp" + android:textColor="@android:color/black" + android:background="@drawable/btn_brightness_bg" + android:clickable="true" + android:focusable="true" /> - - - - - - + android:layout_marginLeft="20dp" + android:layout_marginRight="20dp" + android:text="100%" + android:textSize="16sp" + android:textColor="@android:color/black" /> - - + android:id="@+id/tv_brightness_plus" + android:layout_width="40dp" + android:layout_height="40dp" + android:gravity="center" + android:text="+" + android:textSize="20sp" + android:textColor="@android:color/black" + android:background="@drawable/btn_brightness_bg" + android:clickable="true" + android:focusable="true" /> - + + android:gravity="center_horizontal"> + android:text="取消" + android:textSize="16sp" + android:textColor="@android:color/black" + android:background="@drawable/btn_cancel_bg" + android:layout_marginRight="20dp" + android:clickable="true" + android:focusable="true" /> + android:text="确认" + android:textSize="16sp" + android:textColor="@android:color/white" + android:background="@drawable/btn_confirm_bg" + android:clickable="true" + android:focusable="true" />