说来奇怪,鸿蒙官方并没有提供SeekBar实现,三方库也只有一个一年前的库,和常用方案也不贴合。
费劲实现了个,各位酌情取用。
GIF效果

代码
TypeScript
/**
* 滑动组件
*/
@ComponentV2
export struct SeekBar {
// 最小
@Param
min: number = 0
// 最大
@Param
max: number = 100
// 当前
@Param @Require
value: number
// value改变回调
@Param @Require
onChanged: (value: number) => void
/**
* 回调步伐
* 满足步伐的数字会触发回调
* 最大最小值时,不满足步伐也会触发
*/
@Param
step: number = 1
// thumb大小
@Param
thumbSize: number = 25
// thumb图片
@Param
thumbImage: Resource = $r('app.media.ic_offer_seek')
// thumb是否展示
@Param
showThumb: boolean = true
// 状态监听
@Param
onStatusChanged: (ing: boolean) => void = () => {
}
// 轨道颜色
@Param
trackColor: ResourceColor = '#F4F4F4'
// 轨道高度
@Param
trackHeight: number = 10
// 轨道圆角
@Param
trackRadius: number = 5
// 进度颜色
@Param
progressColor: ResourceColor = $r('app.color.primary')
// 进度高度
@Param
progressHeight: number = 10
// 进度圆角
@Param
progressRadius: number = 5
// 用户是否可拖动
@Param
canDrag: boolean = true
// 宽度
@Local
allWidth: number = 0
// thumb偏移
@Local
thumbOffset: number = 0
// thumb缩放
@Local
thumbScale: number = 1
// 进度条宽度
@Local
progressWidth: number = 0
/**
* 数据改变监听
*/
@Monitor('allWidth','thumbWidth','min','max','value','thumbSize','showThumb')
onValueChanged() {
if (this.allWidth == 0) {
return
}
let value = Math.min(this.max, Math.max(this.min, this.value))
let offset = (this.allWidth - this.thumbSize) * (value - this.min) / (this.max - this.min)
this.thumbOffset = offset ?? 0
this.progressWidth = this.showThumb ? this.thumbOffset + (this.thumbSize / 2) :
this.allWidth * (value - this.min) / (this.max - this.min)
}
build() {
RelativeContainer() {
Stack()
.width('100%')
.height(this.trackHeight)
.backgroundColor(this.trackColor)
.borderRadius(this.trackRadius)
.alignRules({
center: {
anchor: '__container__',
align: VerticalAlign.Center
},
})
Stack()
.height(this.progressHeight)
.width(this.progressWidth)
.borderRadius(this.progressRadius)
.backgroundColor(this.progressColor)
.alignRules({
center: {
anchor: '__container__',
align: VerticalAlign.Center
}
})
Image(this.thumbImage)
.width(this.thumbSize)
.height(this.thumbSize)
.alignRules({
center: {
anchor: '__container__',
align: VerticalAlign.Center
},
})
.margin({ left: this.thumbOffset })
.scale({
x: this.thumbScale,
y: this.thumbScale
})
.visibility(this.showThumb ? Visibility.Visible : Visibility.None)
}
.width('100%')
.height(Math.max(this.progressHeight, this.trackHeight, this.showThumb ? this.thumbSize : 0))
.onSizeChange((oldValue: SizeOptions, newValue: SizeOptions) => {
this.allWidth = (newValue.width as number) || 0
})
.onTouch((event) => {
if (!this.canDrag) {
return
}
let x = event.touches[0].x
let offset = Math.min(Math.max(0, x), this.allWidth - this.thumbSize)
let value =
Math.round(this.min + (this.max - this.min) * offset / (this.allWidth - this.thumbSize))
// 值没有改变,不触发回调
if (value == this.value) {
this.callBackStatus(event)
return
}
if ((Math.abs(value - this.value) >= this.step && Math.abs(value - this.value) % this.step == 0)
|| value == this.min || value == this.max) {
this.onChanged(value)
}
this.callBackStatus(event)
})
}
private callBackStatus(event: TouchEvent) {
if (event.type == TouchType.Down) {
this.thumbScale = 1.2
this.onStatusChanged(true)
} else if (event.type == TouchType.Up) {
this.thumbScale = 1
this.onStatusChanged(false)
}
}
}
使用示例
TypeScript
import { SeekBar } from '../component/SeekBar'
@Entry
@ComponentV2
struct SeekBarPage {
@Local value1: number = 0
@Local value2: number = 0
build() {
List() {
ListItem() {
Column() {
Text('默认: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
}
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('隐藏 thumb: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
showThumb: false
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('更换 thumb: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
thumbImage: $r('app.media.app_icon')
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('颜色互换: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
trackColor: '#006EFF',
progressColor: '#F4F4F4'
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('步伐为10: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
step: 10
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('不可拖动: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
canDrag: false
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('宽椭圆形: ' + this.value1)
.fontSize(16)
SeekBar({
value: this.value1,
onChanged: (value: number) => {
this.value1 = value
},
showThumb: false,
progressHeight: 40,
progressRadius: 20,
trackHeight: 40,
trackRadius: 20
})
.width(200)
.margin({ top: 10 })
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
ListItem() {
Column() {
Text('设置最大666,最小333: ' + this.value2)
.fontSize(16)
SeekBar({
value: this.value2,
onChanged: (value: number) => {
this.value2 = value
},
max: 666,
min: 333
})
.width('100%')
.margin({ top: 10 })
}
.alignItems(HorizontalAlign.Start)
.margin({ top: 5, bottom: 5 })
.backgroundColor($r('sys.color.white'))
.borderRadius(10)
.padding(10)
}
}
.padding(10)
.height('100%')
.width('100%')
.backgroundColor('#F5F6F7')
}
onActionClick(text: string) {
}
}
代码库
完活.