为一篇 HarmonyOS7 教程文章绘制手绘笔记风信息图,主题是“SliderStepMarks

前言

很多刚接触 HarmonyOS7 的同学,第一次用 Slider 都会把它理解成“拖一拖拿到一个数字”。这话不算错,但只说到了一半。

真正做页面的时候,你很快就会发现,不是所有数字都适合让用户随便拖。像评分、档位、百分比这类值,很多时候本来就是离散的,用户需要的不是“任意值”,而是“固定几个可选结果”。

这时候,带刻度的滑块就特别好用。

这篇文章就用这个案例,带你把两个常见场景讲透:

  • 一个是 1 到 5 星的评分选择。
  • 一个是 0% 到 100% 的进度档位选择。

你把这篇看懂,后面再写亮度调节、筛选阈值、等级选择这类页面,会轻松很多。

先看这个案例到底做了什么

为这篇 HarmonyOS7 技术教程绘制手绘蓝图式框架图,主题是“刻度滑块的工作机制”。画面中心是

这个页面表面上只有两个滑块,但它其实在演示两种很典型的离散输入:

  1. 上面的滑块表示评分,范围是 1 ~ 5,步长是 1
  2. 下面的滑块表示百分比,范围是 0 ~ 100,步长是 25

也就是说,用户并不是在拖一个“连续变化”的值,而是在几个固定档位之间选择。

这正是 showSteps(true) 有意义的地方。

如果没有刻度,用户虽然也能拖动,但对“当前到底停在哪个档位”不会那么直观。加上刻度以后,页面会一下子变得更像一个真实控件,而不是纯演示效果。

这种场景为什么适合用刻度滑块

先别急着背 API,先想业务。

比如下面这些场景:

场景 适合原因
星级评分 结果通常只有 1 星到 5 星,天然是离散值
百分比档位选择 比如 0%、25%、50%、75%、100% 这种固定粒度
音量/亮度分级 可以按固定段位调,而不是完全自由拖动
筛选阈值 比如价格等级、距离范围、风险等级

这些场景有一个共同点:

用户最终要的不是“尽量精确的一个值”,而是“落在某个明确档位上”。

所以如果你做的是这类页面,Slider + 步长 + 刻度 会比普通自由滑动更合适。

先抓住这个案例里的两个核心状态

这份代码里最关键的状态就两个:

为文章中“评分和百分比滑块如何交互”的部分绘制手绘流程图,采用 sketch-notes 风格。流程

@State value1: number = 3
@State value2: number = 50

它们分别负责:

  • value1:保存当前评分值
  • value2:保存当前百分比值

你可以把它们理解成两个“结果仓库”。

只要用户一拖滑块,新的值就会写进这两个状态里。状态一改,界面上的文案也会跟着变。

为什么这里要用 @State

因为滑块是可以交互的。

用户每次拖动,页面都要立即更新:

  • 上面的“评分: 3 星”要变成新的值
  • 下面的“进度: 50%”也要变成新的值

如果不用 @State,你改了值,页面不会自动刷新。

所以在 ArkUI 里,只要某个值会随着用户操作变化,并且变化后要反映到界面上,它通常就应该是状态。

先看完整代码

先把完整代码看一遍,后面讲解时你更容易对得上:

/**
 * SliderStepMarks - Slider 刻度标记
 */
import { PRESET_COLORS, generateListItems, PAGE_BG_COLOR, PANEL_BG_COLOR, ACCENT_COLOR, MUTED_TEXT_COLOR } from './types'

@Entry
@Component
struct SliderStepMarks {
  @State isShow: boolean = true
  @State value1: number = 3
  @State value2: number = 50

  build() {
    Column() {
      if (this.isShow) {
        Column() {
          Text('Slider 刻度标记').fontSize(16).fontWeight(FontWeight.Bold).margin({ bottom: 16 })

          Text(`评分: ${this.value1.toFixed(0)}`).fontSize(14).margin({ bottom: 8 })
          Slider({ value: this.value1, min: 1, max: 5, step: 1 })
            .showSteps(true)
            .onChange((v: number) => { this.value1 = v })
            .selectedColor(PRESET_COLORS[2].value)
            .blockColor(PRESET_COLORS[2].value)
            .width('100%').margin({ bottom: 24 })

          Row() {
            Text('1').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('2').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('3').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('4').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('5').fontSize(11).fontColor(MUTED_TEXT_COLOR)
          }.width('100%').justifyContent(FlexAlign.SpaceBetween).margin({ bottom: 28 })

          Text(`进度: ${this.value2.toFixed(0)}%`).fontSize(14).margin({ bottom: 8 })
          Slider({ value: this.value2, min: 0, max: 100, step: 25 })
            .showSteps(true)
            .onChange((v: number) => { this.value2 = v })
            .selectedColor(ACCENT_COLOR)
            .blockColor(ACCENT_COLOR)
            .width('100%').margin({ bottom: 24 })

          Row() {
            Text('0%').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('25%').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('50%').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('75%').fontSize(11).fontColor(MUTED_TEXT_COLOR)
            Text('100%').fontSize(11).fontColor(MUTED_TEXT_COLOR)
          }.width('100%').justifyContent(FlexAlign.SpaceBetween)
        }
        .width('100%').padding(16).backgroundColor(PANEL_BG_COLOR).borderRadius(12)
      }
      Text('Slider 刻度标记')
        .fontSize(12).fontColor(MUTED_TEXT_COLOR).margin({ top: 12 })
    }
    .width('100%').height('100%').backgroundColor(PAGE_BG_COLOR).padding(16)
  }
}

第一眼先看 Slider 的 4 个关键参数

上面评分滑块这一行最值得你停下来多看两眼:

Slider({ value: this.value1, min: 1, max: 5, step: 1 })

这 4 个参数决定了这个滑块的基本行为:

参数 作用 这篇里的值
value 当前默认值 this.value1
min 最小值 1
max 最大值 5
step 每次变化的步长 1

把它翻成大白话就是:

  • 这个评分滑块最小只能到 1
  • 最大只能到 5
  • 每次只能按 1 星递增或递减

所以它最终只会停在 1、2、3、4、5 这几个位置上。

下面这个百分比滑块也是同样思路:

Slider({ value: this.value2, min: 0, max: 100, step: 25 })

它的结果只会落在:

  • 0
  • 25
  • 50
  • 75
  • 100

这就是“步长”在这个案例里的实际意义。

showSteps(true) 为什么是这篇的灵魂

看这行:

.showSteps(true)

它的作用就是把步长刻度显式展示出来。

你可以把它理解成:“不仅内部按固定档位走,而且把这些档位也直接画给用户看。”

这一点特别重要。

因为有些时候,开发者知道这个控件只能选固定档位,但用户未必知道。没有刻度时,用户会感觉自己在拖一个普通滑块;有了刻度以后,用户会更自然地理解:

  • 哦,这是分档选择
  • 哦,它不是连续值
  • 哦,我只能落在几个明确位置上

这就是为什么我说,这一篇真正要理解的,不只是 Slider,而是怎么把“交互规则”清楚地表达给用户。

onChange 到底把什么连起来了

看这两段:

.onChange((v: number) => { this.value1 = v })
.onChange((v: number) => { this.value2 = v })

这两行是整个交互真正“活起来”的地方。

它们做的事情其实非常直接:

  1. 用户拖动滑块。
  2. 新值通过回调传进来。
  3. 把新值写回状态。
  4. 页面文案自动刷新。

这就是一个最标准的“输入 -> 状态 -> 显示”的流程。

如果你是小白,建议你把这条线牢牢记住。后面学 RatingTextPickerDatePickerTimePicker,本质上都是这一套。

为什么文案要跟着滑块实时变化

看这两行:

Text(`评分: ${this.value1.toFixed(0)}`)
Text(`进度: ${this.value2.toFixed(0)}%`)

很多人写示例时,会把滑块放上去就算完事。

但如果没有这些文案,用户虽然能拖,却不一定第一时间知道自己选中了什么值。

尤其是百分比、等级、评分这类输入,拖动结束后最好要有明确结果反馈。

所以这里的文案不是装饰,它承担的是“结果回显”的职责。

toFixed(0) 为什么也值得留意

这里用了:

this.value1.toFixed(0)
this.value2.toFixed(0)

它的作用是把结果格式化成不带小数的整数显示。

虽然这篇里的步长本来就是整数,但这里依然用了 toFixed(0),好处是显示结果更稳定,也更直观。

如果后面你把步长改成 0.50.1 这种更细的粒度,就要重新考虑显示格式,比如保留 1 位小数。

为什么还额外写了两排标签

看这两块 Row

Row() {
  Text('1')
  Text('2')
  Text('3')
  Text('4')
  Text('5')
}
Row() {
  Text('0%')
  Text('25%')
  Text('50%')
  Text('75%')
  Text('100%')
}

这两排文字不是多余的。

showSteps(true) 会把刻度点显示出来,但刻度点本身不一定自带可读标签。对初学者或者普通用户来说,看到刻度点,不一定能马上把它和具体数值对应上。

所以这里额外补了一排说明文字,体验会更完整。

这也是一个很真实的开发思路:

控件本身提供基础交互,但页面往往还需要补足“说明层”。

这个页面的布局为什么很适合入门

这篇的布局结构很简单:

  • 外层一个 Column
  • 中间一个卡片容器
  • 卡片里先放评分模块,再放百分比模块
  • 每个模块都是“结果文案 + 滑块 + 刻度标签”三件套

这种布局很适合小白,因为它清楚、顺序强、没有太多干扰。

你看代码时不会被复杂嵌套绕晕,能把注意力集中在交互逻辑本身。

这篇最值得单独记住的 3 处代码

1. @State value1: number = 3

@State value1: number = 3

它保存的是评分结果。

你拖评分滑块时,真正变的就是它。

2. Slider({ value: this.value1, min: 1, max: 5, step: 1 })

Slider({ value: this.value1, min: 1, max: 5, step: 1 })

这一行决定了评分滑块的规则边界。

如果没有 minmaxstep 这些限制,评分就不会这么明确地落在固定档位上。

3. .onChange((v: number) => { this.value2 = v })

.onChange((v: number) => { this.value2 = v })

这行代码把“用户拖动”真正变成了“页面结果更新”。

没有它,滑块就只是看起来能动,结果却不会写回状态。

小白最容易踩的坑

1. 步长和标签对不上

比如你把 step 写成 20,下面标签却还是 0%、25%、50%、75%、100%

这样页面会很别扭,用户也会困惑。

所以只要你改了步长,就要顺手检查标签是不是也该一起改。

2. 只改滑块,不做结果回显

如果没有上面的“评分”和“进度”文案,交互会显得很空。尤其在业务页面里,用户通常需要一个明确结果,而不是只看一个滑块位置。

3. 一个结果拆成多个状态存

如果评分数字一份状态、星级展示一份状态、提交参数再一份状态,后面很容易不同步。

一个结果,尽量由一个核心状态负责。

4. 忽略 minmaxstep 的组合关系

有些同学会只改一个值,比如把 max 改成 10,但下面的标签和展示文案都没跟着改。页面虽然能跑,但逻辑已经不统一了。

这篇代码能怎么改成真实业务

你完全可以把这个案例继续改成这些页面:

业务方向 改法
商品评分页 保留上半部分评分滑块,拖动后同步展示“非常满意 / 一般 / 不推荐”等文案
系统设置页 把百分比滑块改成音量、亮度、字号调节
筛选面板 用滑块选择价格范围、距离档位、风险等级
问卷页面 让用户用滑块选择满意度、推荐意愿、使用频率

你会发现,它虽然只是一个基础案例,但可迁移性很强。

动手改一改

如果你已经读懂了,建议你顺手做几个小练习:

  • 把评分滑块的范围改成 1 ~ 10
  • 把百分比滑块的步长改成 10,再把标签同步调整。
  • 给评分结果增加一行描述文案,比如 1-2 星 = 不满意4-5 星 = 满意
  • 把两个滑块拆成两个独立小卡片,练习组件拆分。

这些改动不复杂,但非常适合巩固今天这篇的理解。

写在最后

这篇案例表面上讲的是 Slider,其实更重要的是让你理解:当一个值本来就应该按固定档位变化时,界面也应该把这种规则清楚地表达出来。

如果你是 HarmonyOS7 初学者,我建议你不要只看代码,最好自己动手把 step、标签文案、结果回显都改一遍。等你能真正把“步长、刻度、状态、回显”这四件事串起来,后面再做更复杂的交互页面,思路会稳很多。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐