源码整理
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user