请问鸿蒙有没有双向可滑动的控件来选择区间的呀
**已解决
找了一圈api只有找到Slider这个 请问有没有解决办法大佬们求助
2
14
浏览602 发布于2024-08-06 16:54湖南
全部评论
最多点赞
最新发布
最早发布
醉心晴抒
|
**采纳答复
**
弄了个差不多的,仅供参考:
import { MeasureText } from '@kit.ArkUI'
@Component
export struct DualSliders {
/**
* 圆形滑块的大小
*/
circleSize = 25
paddingLeftRight = 15
/**
* 整个组件的宽度
*/
componentWidth = 0
/**
* 滑轨的总宽度
*/
sliderWidth = 0
/**
* 左边圆形滑块的X轴偏移量
*/
@State leftOffsetX: number = 0
/**
* 右边圆形滑块的X轴偏移量
*/
@State rightOffsetX: number = 0
/**
* 左边圆形滑块的最终位置
*/
@State leftPositionX: number = 0
/**
* 右边圆形滑块的最终位置
*/
@State rightPositionX: number = 0
/**
* 左边进度条的宽度
*/
@State leftLineWidth: number = 0
/**
* 中间进度条的宽度
*/
@State middleLineWidth: number = 0
/**
* 右边进度条的宽度
*/
@State rightLineWidth: number = 0
/**
* 刻度0的位置
*/
@State positionX0: number = 0
/**
* 刻度30的位置
*/
@State positionX30: number = 0
/**
* 刻度60的位置
*/
@State positionX60: number = 0
/**
* 刻度90的位置
*/
@State positionX90: number = 0
/**
* 刻度100的位置
*/
@State positionX100: number = 0
/**
* 左边圆形滑块滑动时展示的进度值
*/
@Link leftValue: string
/**
* 右边圆形滑块滑动时展示的进度值
*/
@Link rightValue: string
/**
* 左边圆形滑块是否在拖拽
*/
@State isLeftCircleDragging: boolean = false
/**
* 右边圆形滑块是否在拖拽
*/
@State isRightCircleDragging: boolean = false
/**
* 左边进度值文本的X轴偏移量
*/
@State leftValueOffsetX: number = 0
/**
* 右边进度值文本的X轴偏移量
*/
@State rightValueOffsetX: number = 0
/**
* 滑块滑动时进度值文本的宽度
*/
progressTextWidth = 40
leftDefValue = 30
rightDefValue = 60
initComponents() {
this.sliderWidth = this.componentWidth - 2 * this.paddingLeftRight
let leftValue = parseInt(this.leftValue)
let rightValue = parseInt(this.rightValue)
let minValue = this.circleSize / this.sliderWidth * 100
let isSetInvalid =
Number.isNaN(leftValue) || Number.isNaN(rightValue) || leftValue < 0 || rightValue > 100 ||
rightValue < minValue || leftValue + minValue > rightValue
if (isSetInvalid) {
// 无效设置,使用默认值
leftValue = this.leftDefValue
rightValue = this.rightDefValue
this.leftValue = `${this.leftDefValue}`
this.rightValue = `${this.rightDefValue}`
}
this.leftOffsetX = leftValue / 100 * this.sliderWidth - this.circleSize / 2
this.leftPositionX = this.leftOffsetX
this.rightOffsetX = rightValue / 100 * this.sliderWidth - this.circleSize / 2
this.rightPositionX = this.rightOffsetX
this.leftValueOffsetX = this.leftOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
this.rightValueOffsetX = this.rightOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
this.leftLineWidth = this.leftPositionX + this.circleSize / 2
this.middleLineWidth = this.rightPositionX - this.leftPositionX
this.rightLineWidth = this.sliderWidth - this.rightOffsetX - this.circleSize / 2
let width0 = px2vp(MeasureText.measureText({ textContent: '0' }))
let width30 = px2vp(MeasureText.measureText({ textContent: '30' }))
let width60 = px2vp(MeasureText.measureText({ textContent: '60' }))
let width90 = px2vp(MeasureText.measureText({ textContent: '90' }))
let width100 = px2vp(MeasureText.measureText({ textContent: '100' }))
this.positionX0 = this.paddingLeftRight - width0 / 2
this.positionX30 = this.paddingLeftRight + this.sliderWidth * 0.3 - width30 / 2
this.positionX60 = this.paddingLeftRight + this.sliderWidth * 0.6 - width60 / 2
this.positionX90 = this.paddingLeftRight + this.sliderWidth * 0.9 - width90 / 2
this.positionX100 = this.paddingLeftRight + this.sliderWidth - width100 / 2
}
build() {
RelativeContainer() {
Row() {
Text('0').fontColor(Color.Grey).position({ x: this.positionX0 })
Text('30').fontColor(Color.Green).position({ x: this.positionX30 })
Text('60').fontColor(Color.Grey).position({ x: this.positionX60 })
Text('90').fontColor(Color.Grey).position({ x: this.positionX90 })
Text('100').fontColor(Color.Grey).position({ x: this.positionX100 })
}.id('numbers')
Row() {
Divider().width(this.leftLineWidth).backgroundColor(Color.Grey).height(3)
Divider().width(this.middleLineWidth).backgroundColor(Color.Blue).height(3)
Divider().width(this.rightLineWidth).backgroundColor(Color.Grey).height(3)
}.id('line')
.alignRules({ center: { anchor: 'left_circle', align: VerticalAlign.Center } })
Circle()
.width(this.circleSize)
.height(this.circleSize)
.fill(Color.White)
.stroke(Color.Gray)
.id('left_circle')
.alignRules({ top: { anchor: 'numbers', align: VerticalAlign.Bottom } })
.margin({ top: 20 })
.translate({ x: this.leftOffsetX })
.gesture(PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(_ => this.isLeftCircleDragging = true)
.onActionUpdate(event => {
if (event) {
let offsetX = this.leftPositionX + event.offsetX
if (offsetX < -this.circleSize / 2) {
this.leftOffsetX = -this.circleSize / 2
} else if (offsetX > this.rightPositionX - this.circleSize) {
this.leftOffsetX = this.rightPositionX - this.circleSize
} else {
this.leftOffsetX = offsetX
}
this.leftLineWidth = this.leftOffsetX + this.circleSize / 2
this.middleLineWidth = this.rightPositionX - this.leftOffsetX
this.leftValue = (this.leftLineWidth / this.sliderWidth * 100).toFixed()
this.leftValueOffsetX = this.leftOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
}
}).onActionEnd(() => {
this.leftPositionX = this.leftOffsetX
this.isLeftCircleDragging = false
}))
Circle()
.width(this.circleSize)
.height(this.circleSize)
.stroke(Color.Gray)
.fill(Color.White)
.id('right_circle')
.alignRules({ top: { anchor: 'left_circle', align: VerticalAlign.Top } })
.translate({ x: this.rightOffsetX })
.gesture(PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(_ => this.isRightCircleDragging = true)
.onActionUpdate(event => {
if (event) {
let offsetX = this.rightPositionX + event.offsetX
if (offsetX < this.leftOffsetX + this.circleSize) {
this.rightOffsetX = this.leftPositionX + this.circleSize
} else if (offsetX > this.sliderWidth - this.circleSize / 2) {
this.rightOffsetX = this.sliderWidth - this.circleSize / 2
} else {
this.rightOffsetX = offsetX
}
this.middleLineWidth = this.rightOffsetX - this.leftPositionX
this.rightLineWidth = this.sliderWidth - this.rightOffsetX - this.circleSize / 2
this.rightValue = ((this.leftLineWidth + this.middleLineWidth) / this.sliderWidth * 100).toFixed()
this.rightValueOffsetX = this.rightOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
}
}).onActionEnd(() => {
this.rightPositionX = this.rightOffsetX
this.isRightCircleDragging = false
}))
Text(this.leftValue)
.id('left_value')
.backgroundColor(Color.Orange)
.translate({ x: this.leftValueOffsetX })
.width(this.progressTextWidth)
.textAlign(TextAlign.Center)
.alignRules({ bottom: { anchor: 'left_circle', align: VerticalAlign.Top } })
.visibility(this.isLeftCircleDragging ? Visibility.Visible : Visibility.Hidden)
.margin({ bottom: 2 })
Text(this.rightValue)
.backgroundColor(Color.Orange)
.id('right_value')
.width(this.progressTextWidth)
.textAlign(TextAlign.Center)
.translate({ x: this.rightValueOffsetX })
.alignRules({ bottom: { anchor: 'right_circle', align: VerticalAlign.Top } })
.visibility(this.isRightCircleDragging ? Visibility.Visible : Visibility.Hidden)
.margin({ bottom: 2 })
}
.width('100%')
.padding({ left: this.paddingLeftRight, right: this.paddingLeftRight })
.height('auto')
.onAreaChange((_, newArea) => {
this.componentWidth = newArea.width as number
this.initComponents()
})
}
}
可以这么用:
@Entry
@Component
struct Index {
@State leftValue: string = '20'
@State rightValue: string = '50'
build() {
Column() {
DualSliders({ leftValue: $leftValue, rightValue: $rightValue })
}
.width('100%')
.height('100%')
}
}
希望能满足你的要求,效果大致如下:
import { MeasureText } from '@kit.ArkUI'
@Component
export struct DualSliders {
/**
* 左边圆形滑块的X轴偏移量
*/
@State leftOffsetX: number = 0
/**
* 右边圆形滑块的X轴偏移量
*/
@State rightOffsetX: number = 0
/**
* 左边圆形滑块的最终位置
*/
@State leftPositionX: number = 0
/**
* 右边圆形滑块的最终位置
*/
@State rightPositionX: number = 0
/**
* 左边进度条的宽度
*/
@State leftLineWidth: number = 0
/**
* 中间进度条的宽度
*/
@State middleLineWidth: number = 0
/**
* 右边进度条的宽度
*/
@State rightLineWidth: number = 0
/**
* 上方刻度值文本的位置
*/
@State scaleValuesPosition: number[] = []
/**
* 左边圆形滑块滑动时展示的进度值
*/
@Link leftValue: number
/**
* 右边圆形滑块滑动时展示的进度值
*/
@Link rightValue: number
/**
* 左边圆形滑块是否在拖拽
*/
@State isLeftCircleDragging: boolean = false
/**
* 右边圆形滑块是否在拖拽
*/
@State isRightCircleDragging: boolean = false
/**
* 左边进度值文本的X轴偏移量
*/
@State leftValueOffsetX: number = 0
/**
* 右边进度值文本的X轴偏移量
*/
@State rightValueOffsetX: number = 0
/**
* 滑轨两边未选中颜色
*/
sliderNormalColor: ResourceColor = Color.Grey
/**
* 滑轨中间选中颜色
*/
sliderSelectColor: ResourceColor = Color.Blue
/**
* 滑轨高度
*/
sliderHeight: Length = 3
/**
* 圆形滑块的大小
*/
circleSize = 25
/**
* 左右padding
*/
paddingLeftRight = 15
/**
* 滑块滑动时进度值文本的宽度
*/
progressTextWidth = 40
/**
* 需要显示的刻度值文本数组,从小到大依次排列
*/
scaleValues: number[] = [0, 30, 60, 90, 100]
maxValue: number = 100
minValue: number = 0
range = this.maxValue - this.minValue
/**
* 组件大小初次确定时才进行初始化
*/
isInitFinish = false
/**
* 上方刻度显示的粒度,调用的是浮点数的toFixed(),整数值
*/
fractionDigits = 0
/**
* 整个组件的宽度
*/
componentWidth = 0
/**
* 滑轨的总宽度
*/
sliderWidth = 0
/**
* 上方刻度值文本颜色
*/
scaleTextColors: ResourceColor[] = [Color.Grey, Color.Grey, Color.Grey, Color.Grey, Color.Grey]
useDefaultValues() {
this.leftValue = 30
this.rightValue = 60
this.maxValue = 100
this.minValue = 0
this.range = this.maxValue - this.minValue
this.scaleValues = [0, 30, 60, 90, 100]
this.fractionDigits = 0
this.scaleTextColors = [Color.Grey, Color.Grey, Color.Grey, Color.Grey, Color.Grey]
}
initComponents() {
if (this.isInitFinish) {
return
}
this.isInitFinish = true
this.sliderWidth = this.componentWidth - 2 * this.paddingLeftRight
this.range = this.maxValue - this.minValue
let minRange = this.circleSize / this.sliderWidth * this.range
let isSetInvalid = this.leftValue < this.minValue || this.rightValue > this.maxValue ||
this.rightValue < minRange || this.leftValue + minRange > this.rightValue
if (isSetInvalid) {
// 无效设置,使用默认值
this.useDefaultValues()
}
this.leftOffsetX = (this.leftValue - this.minValue) / this.range * this.sliderWidth - this.circleSize / 2
this.leftPositionX = this.leftOffsetX
this.rightOffsetX = (this.rightValue - this.minValue) / this.range * this.sliderWidth - this.circleSize / 2
this.rightPositionX = this.rightOffsetX
this.leftValueOffsetX = this.leftOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
this.rightValueOffsetX = this.rightOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
this.leftLineWidth = this.leftPositionX + this.circleSize / 2
this.middleLineWidth = this.rightPositionX - this.leftPositionX
this.rightLineWidth = this.sliderWidth - this.rightOffsetX - this.circleSize / 2
this.scaleValues.forEach(scale => {
let width = px2vp(MeasureText.measureText({ textContent: `${scale}` }))
let position = this.paddingLeftRight + this.sliderWidth * (scale - this.minValue) / this.range - width / 2
this.scaleValuesPosition.push(position)
})
}
build() {
RelativeContainer() {
Row() {
ForEach(this.scaleValuesPosition, (item: number, index: number) => {
Text(`${this.scaleValues[index]}`).fontColor(this.scaleTextColors[index]).position({ x: item })
})
}.id('numbers')
Row() {
Divider().width(this.leftLineWidth).backgroundColor(this.sliderNormalColor).height(this.sliderHeight)
Divider().width(this.middleLineWidth).backgroundColor(this.sliderSelectColor).height(this.sliderHeight)
Divider().width(this.rightLineWidth).backgroundColor(this.sliderNormalColor).height(this.sliderHeight)
}.id('line')
.alignRules({ center: { anchor: 'left_circle', align: VerticalAlign.Center } })
Circle()
.width(this.circleSize)
.height(this.circleSize)
.fill(Color.White)
.stroke(Color.Gray)
.id('left_circle')
.alignRules({ top: { anchor: 'numbers', align: VerticalAlign.Bottom } })
.margin({ top: 20 })
.translate({ x: this.leftOffsetX })
.gesture(PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(_ => this.isLeftCircleDragging = true)
.onActionUpdate(event => {
if (event) {
let offsetX = this.leftPositionX + event.offsetX
if (offsetX < -this.circleSize / 2) {
this.leftOffsetX = -this.circleSize / 2
} else if (offsetX > this.rightPositionX - this.circleSize) {
this.leftOffsetX = this.rightPositionX - this.circleSize
} else {
this.leftOffsetX = offsetX
}
this.leftLineWidth = this.leftOffsetX + this.circleSize / 2
this.middleLineWidth = this.rightPositionX - this.leftOffsetX
this.leftValue = this.leftLineWidth / this.sliderWidth * this.range + this.minValue
this.leftValueOffsetX = this.leftOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
}
}).onActionEnd(() => {
this.leftPositionX = this.leftOffsetX
this.isLeftCircleDragging = false
}))
Circle()
.width(this.circleSize)
.height(this.circleSize)
.stroke(Color.Gray)
.fill(Color.White)
.id('right_circle')
.alignRules({ top: { anchor: 'left_circle', align: VerticalAlign.Top } })
.translate({ x: this.rightOffsetX })
.gesture(PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(_ => this.isRightCircleDragging = true)
.onActionUpdate(event => {
if (event) {
let offsetX = this.rightPositionX + event.offsetX
if (offsetX < this.leftOffsetX + this.circleSize) {
this.rightOffsetX = this.leftPositionX + this.circleSize
} else if (offsetX > this.sliderWidth - this.circleSize / 2) {
this.rightOffsetX = this.sliderWidth - this.circleSize / 2
} else {
this.rightOffsetX = offsetX
}
this.middleLineWidth = this.rightOffsetX - this.leftPositionX
this.rightLineWidth = this.sliderWidth - this.rightOffsetX - this.circleSize / 2
this.rightValue =
(this.leftLineWidth + this.middleLineWidth) / this.sliderWidth * this.range + this.minValue
this.rightValueOffsetX = this.rightOffsetX + this.circleSize / 2 - this.progressTextWidth / 2
}
}).onActionEnd(() => {
this.rightPositionX = this.rightOffsetX
this.isRightCircleDragging = false
}))
Text(this.leftValue.toFixed(this.fractionDigits))
.id('left_value')
.backgroundColor(Color.Orange)
.translate({ x: this.leftValueOffsetX })
.width(this.progressTextWidth)
.textAlign(TextAlign.Center)
.alignRules({ bottom: { anchor: 'left_circle', align: VerticalAlign.Top } })
.visibility(this.isLeftCircleDragging ? Visibility.Visible : Visibility.Hidden)
.margin({ bottom: 2 })
Text(this.rightValue.toFixed(this.fractionDigits))
.backgroundColor(Color.Orange)
.id('right_value')
.width(this.progressTextWidth)
.textAlign(TextAlign.Center)
.translate({ x: this.rightValueOffsetX })
.alignRules({ bottom: { anchor: 'right_circle', align: VerticalAlign.Top } })
.visibility(this.isRightCircleDragging ? Visibility.Visible : Visibility.Hidden)
.margin({ bottom: 2 })
}
.width('100%')
.padding({ left: this.paddingLeftRight, right: this.paddingLeftRight })
.height('auto')
.onAreaChange((_, newArea) => {
this.componentWidth = newArea.width as number
this.initComponents()
})
}
}
使用方式:
@Entry
@Component
struct Index {
@State leftValue: number = 40
@State rightValue: number = 70
@State leftValue0: number = 2
@State rightValue0: number = 5
@State leftValue1: number = 0.3
@State rightValue1: number = 0.6
@State leftValue2: number = 18
@State rightValue2: number = 50
@State leftValue3: number = 18
@State rightValue3: number = 50
build() {
Column({ space: 15 }) {
DualSliders({
leftValue: this.leftValue,
rightValue: this.rightValue
})
DualSliders({
leftValue: this.leftValue0,
rightValue: this.rightValue0,
scaleValues: [0, 2, 4, 6, 8, 10],
scaleTextColors: [Color.Grey, Color.Orange, Color.Brown, Color.Blue, Color.Red],
sliderNormalColor: Color.Pink,
sliderSelectColor: Color.Red,
minValue: 0,
maxValue: 10,
fractionDigits: 1
})
DualSliders({
leftValue: this.leftValue1,
rightValue: this.rightValue1,
scaleValues: [0, 0.2, 0.4, 0.6, 0.8, 1],
minValue: 0,
maxValue: 1,
fractionDigits: 2
})
DualSliders({
leftValue: this.leftValue2,
rightValue: this.rightValue2,
scaleValues: [18, 50],
minValue: 18,
maxValue: 50,
fractionDigits: 0
})
DualSliders({
leftValue: this.leftValue3,
rightValue: this.rightValue3,
scaleValues: [18, 50],
minValue: 10,
maxValue: 80,
fractionDigits: 0
})
}
.width('100%')
.height('100%')
.padding({ top: 50 })
}
}
1
3
3楼编辑于2024-08-09 10:00 来自天津
-
方木
**
大佬 可是设置每步走多少吗? 类似于slider的step
2024-08-09 09:21 来自北京
-
醉心晴抒回复方木
**
参考链接:developer.huawei.com/consumer/cn…
2024-08-09 15:07 来自天津
-
哈哈
**
请问下如何设置两个滑块可以重叠呢,默认的值为0和0,滑动时也允许重叠
2024-11-12 09:56 来自北京
恒
**
大佬,请问该怎样限制选中区域呢,例如选中区域最小是10,就不可以再缩小了
4楼回复于2025-02-10 10:58 来自上海
番茄猫
**
0 0 我好像没见过这样的组件
自己写个组件好了 0 0
1楼回复于2024-08-06 17:39 来自上海