源码整理

This commit is contained in:
2025-12-17 14:14:07 +08:00
parent 740ab932a4
commit dbcb5259d9

View File

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