Android如何实现滑动调节百分比的自定义按钮视图联动效果
Android 滑动调节百分比控件实现方案
核心交互逻辑很清晰:把触摸的横向位移映射为0-100的进度值,同步联动蓝色进度条宽度、百分比文本即可,常用实现方案有三种,按需选就行:
- 方案1:纯自定义View实现(性能最优、自由度最高)
不用依赖任何第三方控件,自己继承View或者写组合控件就能做:- 内部布局拆成三层:最底层是固定宽度的灰色背景条,中间层是宽度随进度变化的蓝色进度视图,最上层是居中显示百分比的TextView
- 重写
onTouchEvent处理触摸逻辑:ACTION_DOWN时记录按下的X坐标、当前初始进度,同时通知父容器不要拦截当前触摸事件;ACTION_MOVE时计算和按下点的横向位移,按你设定的滑动灵敏度换算成进度偏移量,把最终进度钳制在0-100的区间内;每次进度更新后直接修改蓝色视图的LayoutParams宽度、同步更新TextView文本,触发重绘就行。
核心触摸逻辑参考:
private var startX = 0f private var lastProgress = 0 private var currentProgress = 0 // 滑动满100%进度对应的像素距离,一般设为控件总宽度即可 private val trackWidth by lazy { width - paddingLeft - paddingRight } override fun onTouchEvent(event: MotionEvent): Boolean { when(event.actionMasked) { MotionEvent.ACTION_DOWN -> { startX = event.x lastProgress = currentProgress parent?.requestDisallowInterceptTouchEvent(true) } MotionEvent.ACTION_MOVE -> { val dx = event.x - startX val deltaProgress = (dx / trackWidth * 100).toInt() currentProgress = (lastProgress + deltaProgress).coerceIn(0, 100) refreshUI() } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { parent?.requestDisallowInterceptTouchEvent(false) } } return true } private fun refreshUI() { progressView.layoutParams = progressView.layoutParams.apply { width = (trackWidth * currentProgress / 100f).toInt() } percentTv.text = "$currentProgress%" } - 方案2:改造系统SeekBar实现(开发成本最低)
系统原生SeekBar本身就自带滑动改进度、进度回调的能力,改下样式就能直接匹配需求,不用自己处理触摸逻辑:- 自定义SeekBar的样式:把默认的拖动块thumb设为透明,把进度条背景设为灰色、已选进度设为蓝色,去掉默认的阴影、波纹效果
- 在SeekBar上方叠一个居中的TextView,给SeekBar设置进度监听,在
onProgressChanged回调里直接拿当前进度更新TextView文本就行 - 这个方案的缺点是样式定制自由度不如纯自定义View,要改比较复杂的圆角、渐变效果会麻烦一点。
- 方案3:配合手势工具类+属性动画实现(动效最顺滑)
如果需要做松手吸附刻度、平滑回弹、进度渐变等复杂动效,可以用这个方案:- 直接用
GestureDetector识别横向滑动手势计算目标进度,或者用ViewDragHelper处理拖拽逻辑,省去自己写边界判断、触摸冲突的代码 - 滑动过程中实时更新进度,松手后如果需要做平滑过渡,就用
ValueAnimator做从当前进度到目标进度的插值动画,动画每一帧回调里更新进度条宽度和文本,动效会比直接刷新更自然
- 直接用
踩坑提醒:如果控件放在ScrollView、ViewPager、RecyclerView这类可滑动的父容器里,一定要在ACTION_DOWN的时候调用
parent.requestDisallowInterceptTouchEvent(true),不然会出现滑到一半事件被父容器抢走的问题;进度换算不要写死像素阈值,按控件自身宽度按比例计算,才能适配所有分辨率的设备。
内容的提问来源于stack exchange,提问作者Adrian Iosep
相关产品推荐
相关产品推荐

