鸿蒙-SeekBar

说来奇怪,鸿蒙官方并没有提供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) {

  }
}

代码库

gitee.com/martin2wss/...

完活.

相关推荐
zhanshuo1 小时前
玩转鸿蒙开发者文档:一文教你高效查资料 + 快速落地功能
harmonyos
zhanshuo1 小时前
鸿蒙 App 也能一键切换深浅色?用 ArkTS 教你轻松实现!
harmonyos
Keya5 小时前
鸿蒙开发样式复用:@Styles、@Extend与AttributeModifier深度对比
前端·分布式·harmonyos
ilmari13 小时前
HarmonyOS 基于Network Kit封装的网络请求工具
android·flutter·harmonyos
大雷神13 小时前
HarmonyOS中调用C/C++代码(NDK)
harmonyos
simple_lau13 小时前
鸿蒙资源加载深度解析:$r与$rawfile的性能差异与最佳实践
harmonyos·arkts·arkui
zkmall15 小时前
移动商城平台适配:ZKmall开源商城鸿蒙 / 小程序端开发要点
小程序·开源·harmonyos
前端菜鸟日常21 小时前
鸿蒙组件装饰器深度解析:@Component vs @ComponentV2
华为·harmonyos
AlbertZein1 天前
HarmonyOS5 源码分析 —— ‘状态管理’如何管理的(1)?
架构·harmonyos