# 如何实现Slider滑块的自定义设置

#### 问题现象

当前Slider组件的滑块，只支持图片和形状的设置，并且只支持Circle、Ellipse、Path、Rect四种形状，无法实现滑块内部内容的自定义。

#### 背景知识

*   blockStyle：设置滑块形状参数。支持type、image、shape三种形式设置。
*   Stack：堆叠容器，子组件按照顺序依次入栈，后一个子组件覆盖前一个子组件。
*   hitTestBehavior：设置组件的触摸测试类型。hitTestBehavior属性设置为Transparent时，自身和子节点都响应触摸测试，不会阻塞兄弟节点的触摸测试，不会影响祖先节点的触摸测试。

#### 解决方案

实现逻辑：Slider组件的滑块不支持自定义布局，本方案使用Stack容器，在Slider的上层覆盖一个与滑块大小相同的Row组件，在Row组件中实现自定义布局内容，滑动滑块时，获取滑块位置传递给Row组件的offset，实现Row组件的同步移动。

1.  设置Slider的滑块类型为SliderBlockType.SHAPE，设置为圆角矩形。颜色设置为透明。
    
    ```
    Slider({ style: SliderStyle.OutSet, value: 0 })
      .width(this.slideWidth)
      .blockSize({ width: this.blockWidth, height: this.blockHeight })
      .blockColor(Color.Transparent)
      .blockStyle({
        type: SliderBlockType.SHAPE,
        shape: new Rect({ width: this.blockWidth, height: this.blockHeight }).radius(5)
      })
    ```
    
2.  在Stack容器中Slider组件的同级，添加Row组件，设置为和步骤1中滑块相同的长宽和圆角，添加自定义内容。
    
    ```
    Row() {
      Text('自定义')
        .fontSize(10);
    }
    .width(this.blockWidth)
    .height(this.blockHeight)
    .borderRadius(5)
    .hitTestBehavior(HitTestMode.Transparent)
    .backgroundColor(Color.Grey)
    .justifyContent(FlexAlign.Center)
    .offset({
      x: this.tipsOffset
    });
    ```
    
3.  通过Slider的onChange事件，获取滑块滑动的百分比。
    
    ```
    Slider({ style: SliderStyle.OutSet, value: 0 })
      .width(this.slideWidth)
      .blockSize({ width: this.blockWidth, height: this.blockHeight })
      .blockColor(Color.Transparent)
      .blockStyle({
        type: SliderBlockType.SHAPE,
        shape: new Rect({ width: this.blockWidth, height: this.blockHeight }).radius(5)
      })
      .onChange((value: number) => {
        this.showTip(value);
      });
    ```
    
4.  通过Slider组件的宽度乘以滑块位置百分比，获取X轴的偏移距离，动态赋值给Row组件的offset，实现Row组件的X轴同步偏移。注意：Slider自带边距，计算偏移时需考虑边距值。
    
    ```
    private showTip(value: number) {
      let percent = Number((value / 100).toFixed(2));
      this.tipsOffset = this.slideMargin + (this.slideWidth - this.blockWidth - this.slideMargin * 2) * percent;
    }
    ```
    
    实现效果：
    
5.  完整代码：
    
    ```
    @Entry
    @Component
    struct SliderExample {
      private slideMargin: number = 4;
      @State tipsOffset: number = this.slideMargin;
      private slideWidth: number = 340;
      private blockWidth: number = 60;
      private blockHeight: number = 20;
    
    
      build() {
        Column() {
          Stack({ alignContent: Alignment.Start }) {
            Slider({ style: SliderStyle.OutSet, value: 0 })
              .width(this.slideWidth)
              .blockSize({ width: this.blockWidth, height: this.blockHeight })
              .blockColor(Color.Transparent)
              .blockStyle({
                type: SliderBlockType.SHAPE,
                shape: new Rect({ width: this.blockWidth, height: this.blockHeight }).radius(5)
              })
              .onChange((value: number) => {
                this.showTip(value);
              });
    
    
            Row() {
              Text('自定义')
                .fontSize(10);
            }
            .width(this.blockWidth)
            .height(this.blockHeight)
            .borderRadius(5)
            .hitTestBehavior(HitTestMode.Transparent)
            .backgroundColor(Color.Grey)
            .justifyContent(FlexAlign.Center)
            .offset({
              x: this.tipsOffset
            });
          }
          .margin({ left: 30, top: 300 });
        };
      }
    
    
      private showTip(value: number) {
        let percent = Number((value / 100).toFixed(2));
        this.tipsOffset = this.slideMargin + (this.slideWidth - this.blockWidth - this.slideMargin * 2) * percent;
      }
    }
    ```