基于Slider的滑动条
在HarmonyOS中,Slider组件是由ArkUI开发框架提供的滑动条组件,通常用于快速调节设置值,如音量调节、亮度调节等应用场景。
场景一:自定义Slider
效果
方案
1. 如上图,其主要的实现方式是对其套上Stack容器,把图片置于Slider的上层,然后主要要对其设置hitTestBehavior(HitTestMode.Transparent),将上层的触摸滑动手势传递到下层的Slider去。
2. 橙色的进度条主要就是利用了滑轨可以设置渐变色,然后边角半径设置的小一点,就可以有上图这个效果;如果不想要填充颜色,可以设置selectColor为Transparent;然后如果想自定义滑块的样式,可以选择将block类型设置为image,自己传入自己想要自定义的图片即可。
案例代码
@Component
export struct WidgetsSliderPage {
private gradientColorSilderTwo: LinearGradient = new LinearGradient([
{ color: '#FFFFFF', offset: 1 },
{ color: '#FFAB5B', offset: 0 }
])
@State rowWidthNumber: number = 333;
@State inSetValueOne: number = 60;
build() {
NavDestination() {
Column() {
Text('带图片UI的进度条').fontSize(16).fontColor('#51CBE0').width('90%').margin(15)
Stack() {
Row() {
Slider({
value: this.inSetValueOne,
min: 0,
max: 100,
style: SliderStyle.InSet
})
.blockColor('#FFFFFF')
.trackColor('#EBEBEB')
.trackThickness(66)
.blockSize({ width: 55, height: 55 })
.selectedColor('#D9D9D9')
.onChange((value: number, mode: SliderChangeMode) => {
this.inSetValueOne = value
console.info('value:' + value + 'mode' + mode.toString)
})
}.width(this.rowWidthNumber)
Row() {
Image($r('app.media.ic_controlcenter_brightness_reduce'))
.width(55).height(55)
Image($r('app.media.ic_controlcenter_brightness_plus'))
.width(55).height(55)
}
.zIndex(1)
.width(this.rowWidthNumber - 25)
.justifyContent(FlexAlign.SpaceBetween)
.hitTestBehavior(HitTestMode.Transparent)
}
Text('圆角纯色渐变进度条').fontSize(16).fontColor('#51CBE0').width('90%').margin(25)
Row() {
Slider({
min: 0,
max: 100,
value: 40,
style: SliderStyle.InSet
})
.selectedColor(Color.Transparent)
.trackThickness(66)
.blockSize({ width: 55, height: 55 })// 设置渐变色
.trackColor(this.gradientColorSilderTwo)
.trackBorderRadius(15)
}.width(this.rowWidthNumber)
}
}
.width('100%')
.height('100%')
.backgroundColor('#c9ffffff')
}
}
场景二:视频进度条缓冲区的实现
效果
效果说明:想要自定义实现视频进度条,Slider组件非常契合,但是Slider只提供选中色和非选中色的设置,而视频进度条还需要一个缓冲区颜色的设置;因此,此场景就是视频进度条的UI实现,下图中橙色部分为选中色,深灰色部分就是缓冲区颜色,浅灰色就是非选中色。
方案
用Slider来自定义视频进度条是开发中比较常见的一种用法,具体的实现方案为用Stack叠加,底层放Progress组件,上层为Slider组件;底层Progress进度条的填充色充当视频进度条的缓冲色,Progress的背景色充当视频进度条的背景色;上层Slider的填充色充当视频进度条已加载的填充色;注意,因为UX设计上Slider就算设置宽度100%也会对左右屏幕会有避让,所以想要和Progress和Slider完美贴合,这就得开发者自己去计算,如下案例代码中会给出示例,请开发者参考计算实现。
建议开发者配合codelab中的视屏播放器demo观看体验更佳,参考:视频播放器(ArkTS)。
案例代码
@Component
export struct VideoSliderPage {
@State displayScreenWidth: number = 0;
aboutToAppear() {
// 获取屏幕的宽度
display.getAllDisplays((err, data) => {
let displayClass: display.Display | null = null;
displayClass = display.getDefaultDisplaySync();
this.displayScreenWidth = px2vp(displayClass.width);
})
}
build() {
NavDestination() {
Column() {
Stack() {
Progress({ value: 70, type: ProgressType.Linear })
// 设置进度条颜色
.color('#D9D9D9')
.backgroundColor('#EBEBEB')
.style({
// 设置进度条宽度,ux设计上Slider的默认高度为4vp
strokeWidth: 4,
enableSmoothEffect: true
})
// 说一下这里为什么要减去24,因为slider左右有避让距离,左右各为12vp,12vp是4vp的阴影加上8vp的block半径
.width(this.displayScreenWidth - 24)
Slider({ style: SliderStyle.OutSet, value: 20 })
.blockStyle({ type: SliderBlockType.DEFAULT })
.trackColor(Color.Transparent)
.selectedColor('#F29200')
.width(this.displayScreenWidth)
}
}
.height('100%')
.width('100%')
.backgroundColor('#fffff')
}
}
}
场景三:视频进度条手指拖动如何更灵敏
效果
效果说明:在我们日常使用视频软件中,通常点击唤出视频进度条的时候期望其响应滑动手势的部分不仅仅局限于进度条这一个部分,期望进度条的周围都可以响应滑动手势;下图主要表达的是拓展了Slider滑动手势的响应范围,红色区域就是原始的Slider响应去,上下橙色和粉色区域都是拓展的手势响应区。
方案
效果中的红色区域是Slider原始的可触摸区域,增大的橙色和粉色区主要思路是通过responseRegion这个全局属性来增大触摸热区,然后通过给热区设置平移手势来控制Slider的value来进而控制Slider的滑动,是一个类似视频软件拖动进度条的场景。
案例代码
@Component
export struct IncreaseTouchAreaSliderPage {
@State text: string = '哈哈哈';
@State value: number = 40;
flagValue: number = 0;
build() {
NavDestination() {
Stack() {
Text(this.text).fontSize(20)
Row() {
}
.onClick(() => {
this.text = '上方横条onclick'
})
.backgroundColor(Color.Orange)
.width('100%')
.height(24)
// 绝对定位,确定子组件相对父组件的位置
.position({ x: 0, y: '100%' })
// 设置子元素在位置定位时的锚点。
.markAnchor({ x: 0, y: 96 })
Row() {
}
.onClick(() => {
this.text = '下方横条onclick'
})
.backgroundColor(Color.Pink)
.width('100%')
.height(24)
.position({ x: 0, y: '100%' })
.markAnchor({ x: 0, y: 48 })
Row() {
Slider({ style: SliderStyle.OutSet, value: this.value })
.blockSize({ width: 6, height: 6 })
.blockBorderColor(Color.White)
.blockBorderWidth(5)
.height(24)
.trackThickness(3)
.backgroundColor(Color.Red)
.onChange((value) => {
this.value = value
})
}
.position({ x: 0, y: '100%' })
.markAnchor({ x: 0, y: 72 })
.backgroundColor('rgba(255, 255,255,0.5)')
// 触摸热区设置:当y设置为'100%'时表示热区往下偏移组件本身高度大小,当y设置为'-100%'时表示热区往上偏移组件本身高度大小。
.responseRegion([{
x: 0,
y: 0,
width: '100%',
height: '100%'
},
{
x: 0,
y: '100%',
width: '100%',
height: '100%'
},
{
x: 0,
y: '-100%',
width: '100%',
height: '100%'
}])
// 触摸测试
.hitTestBehavior(HitTestMode.Transparent)
.gesture(
PanGesture(new PanGestureOptions({ direction: PanDirection.Left | PanDirection.Right }))
.onActionStart(() => {
this.flagValue = this.value
})
.onActionUpdate((event?: GestureEvent) => {
if (event) {
this.value = this.flagValue + (event.offsetX / 3)
}
})
)
}
.backgroundColor(Color.Green)
.width('100%')
.height('100%')
}
}
}
其他常见问题
Q:Pad上父容器响应手势之后,拖动Slider滑杆onChange不会触发mode=moving。
A:可以将gesture改用parallelGesture来使用。
Q:如何获取到Slider的block的坐标。
A:用componentUtils.getRectangleById获取Slider组件的坐标,根据onChange返回的进度值,以及Slider的边距计算block的坐标。