From dbcb5259d9b5fbb821af051b8ea3b531df25c781 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Wed, 17 Dec 2025 14:14:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=BA=90=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../powerbell/views/VerticalSeekBar.java | 147 ++++++++++++------ 1 file changed, 99 insertions(+), 48 deletions(-) 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 09d8a60..91ff70e 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 @@ -6,91 +6,142 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.SeekBar; -public class VerticalSeekBar extends SeekBar { - public static final String TAG = VerticalSeekBar.class.getSimpleName(); +import cc.winboll.studio.libappbase.LogUtils; - public volatile int _mnProgress = -1; - +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/12/17 14:11 + * @Describe 垂直进度条控件,适配 API30,支持逆时针旋转(0在下,100在上),修复滑块同步bug + */ +public class VerticalSeekBar extends SeekBar { + // ======================== 静态常量(置顶,唯一标识)======================== + private static final String TAG = VerticalSeekBar.class.getSimpleName(); + + // ======================== 成员变量(私有优先, volatile 关键字保留,确保线程可见性)======================== + private volatile int mProgress = -1; // 当前进度缓存,修复滑块同步问题 + + // ======================== 构造方法(按参数个数升序排列,适配 Java7 语法)======================== public VerticalSeekBar(Context context) { super(context); - } - - public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + initView(); + LogUtils.d(TAG, "VerticalSeekBar: 单参数构造方法初始化完成"); } public VerticalSeekBar(Context context, AttributeSet attrs) { super(context, attrs); - // 去除冗余的水平阴影 + initView(); + LogUtils.d(TAG, "VerticalSeekBar: 双参数构造方法初始化完成"); + } + + public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initView(); + LogUtils.d(TAG, "VerticalSeekBar: 三参数构造方法初始化完成"); + } + + // ======================== 初始化方法(封装通用逻辑,避免构造方法冗余)======================== + /** + * 初始化视图配置,适配 API30 资源管控 + */ + private void initView() { + // 移除水平默认阴影,优化垂直显示效果,减少 API30 不必要的绘制开销 setBackgroundDrawable(null); - + LogUtils.d(TAG, "initView: 视图初始化完成,移除默认背景阴影"); } - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(h, w, oldh, oldw); - } - - - - + // ======================== 重写测量/布局方法(按执行顺序排列:测量→尺寸变化→绘制)======================== + /** + * 重写测量方法,交换宽高适配垂直显示,兼容 API30 测量机制 + */ @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(heightMeasureSpec, widthMeasureSpec); + // 交换测量结果,将原高度作为宽度、原宽度作为高度,实现垂直布局 setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); + LogUtils.v(TAG, "onMeasure: 垂直测量完成,宽=" + getMeasuredHeight() + ", 高=" + getMeasuredWidth()); } - protected void onDraw(Canvas c) { - // 0--------100,顺时针旋转,小在上 - // c.rotate(+90); - // c.translate(0, -getWidth()); - - // 0--------100,逆时针旋转,小在下 - c.rotate(-90); - c.translate(-getHeight(), 0); - - super.onDraw(c); + /** + * 重写尺寸变化方法,确保进度变化时视图同步刷新,适配 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); } + /** + * 重写绘制方法,逆时针旋转90度实现垂直显示(0在下,100在上),适配 API30 画布渲染 + */ + @Override + protected void onDraw(Canvas canvas) { + // 逆时针旋转90度,平移画布避免绘制偏移(核心垂直显示逻辑) + canvas.rotate(-90); + canvas.translate(-getHeight(), 0); + super.onDraw(canvas); + LogUtils.v(TAG, "onDraw: 垂直绘制完成,旋转角度=-90°"); + } - + // ======================== 重写触摸事件方法(核心交互逻辑,适配 API30 事件分发)======================== + /** + * 重写触摸事件,转换坐标计算垂直进度,确保 OnSeekBarChangeListener 正常回调 + */ @Override public boolean onTouchEvent(MotionEvent event) { - // 调用基类的处理函数 - // 该方法可以使得 - // SeekBar.OnSeekBarChangeListener - // 的 onStopTrackingTouch 和 onStartTrackingTouch 等函数有效。 + // 先调用父类方法,保证 OnSeekBarChangeListener 的 onStart/onStopTrackingTouch 正常触发(关键!) boolean handled = super.onTouchEvent(event); + LogUtils.d(TAG, "onTouchEvent: 触摸事件触发,action=" + event.getAction() + ", 父类处理结果=" + handled); if (handled) { - int action = event.getAction(); - switch (action) { - case MotionEvent.ACTION_UP: + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: + LogUtils.d(TAG, "onTouchEvent: 触摸按下,坐标Y=" + event.getY()); + break; case MotionEvent.ACTION_MOVE: - // 0--------100,顺时针旋转,小在上 - //_mnProgress = (int)(getMax() * event.getY() / getHeight()); - // // 0--------100,逆时针旋转,小在下 - _mnProgress = getMax() - (int) (getMax() * event.getY() / getHeight()); - _mnProgress = _mnProgress > 100 ? 100 : _mnProgress ; - //LogUtils.d(TAG, "_mnProgress is " + Integer.toString(_mnProgress)); - setProgress(_mnProgress); - //onSizeChanged(getWidth(), getHeight(), 0, 0); + // 计算垂直进度(逆时针旋转: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: - break; - default : - //LogUtils.d(TAG, "event.getAction() is " + event.getAction()); + LogUtils.d(TAG, "onTouchEvent: 触摸取消,进度保持=" + getProgress()); break; } } - + // 返回父类处理结果,确保事件分发完整,适配 API30 事件机制 return handled; } - // 解决调用setProgress()方法时滑块不跟随的bug + // ======================== 重写进度设置方法(修复滑块同步bug,适配 API30 进度更新)======================== + /** + * 重写进度设置,调用尺寸变化方法强制刷新,解决 setProgress 滑块不跟随问题 + */ @Override public synchronized void setProgress(int progress) { super.setProgress(progress); + // 强制触发尺寸变化,同步刷新滑块位置(核心bug修复逻辑) onSizeChanged(getWidth(), getHeight(), 0, 0); + mProgress = progress; + LogUtils.d(TAG, "setProgress: 进度设置完成,进度=" + progress + ", 滑块同步刷新"); + } + + // ======================== 内部工具方法(封装重复逻辑,提升复用性)======================== + /** + * 计算垂直进度,校准范围 0~100,避免异常值 + * @param touchY 触摸点Y坐标 + */ + private void calculateProgress(float touchY) { + // 核心进度计算公式(逆时针旋转适配) + mProgress = getMax() - (int) (getMax() * touchY / getHeight()); + // 校准进度范围,防止超出 0~100(兼容 API30 进度边界校验) + mProgress = Math.max(0, Math.min(mProgress, getMax())); + LogUtils.v(TAG, "calculateProgress: 进度计算完成,触摸Y=" + touchY + ", 计算进度=" + mProgress); } } +