1. 项目概述鸿蒙应用开发系列写到第十三篇这次要拆的是ArkUI里两个“看起来很简单、用起来全是细节”的组件——Slider滑动条和Progress进度条。这两个组件算是应用开发里的“老熟人”了做播放器、调节音量、展示下载进度、上传状态、图片裁剪、颜色选择器几乎每个带交互的App都绕不开它们。但说实话我在看很多开发者的代码时发现大多数人只是把它们当成“能拖的条”和“会动的圈”来用连参数都没调透。比如Slider在API 10之后新增的滑块样式变化、步进模式、双滑块范围选择很多人压根不知道。再比如Progress的环形进度那条两端圆帽是怎么控制的、怎么跑出平滑的动画、怎么让进度条和真实网络开销解耦这些细节对于做出一款体验过得去的应用确实是分水岭。这篇文章我把两个组件揉在一起讲因为它们在很多场景下是成对出现的——播放器要同时有进度显示和进度调节上传页面要同时有排队进度和整体进度设置页里也经常是“展示当前值”和“调节新值”两个控件挨在一起。分开学完再合起来练一个实战Demo效果会更好。整个内容分四块先讲设计思路和选型取舍明白什么时候用Slider什么时候用Progress、怎么搭配然后把Slider的参数和事件逐条拆开包括容易踩坑的API版本差异接着逐个解析Progress的类型和装饰细节最后用一个“模拟播放器控制面板”的完整示例收尾再整理一份我在实际调试中遇到的坑和排查经验。阅读本文不需要太多的前置基础只要跟上系列前面的内容、对ArkUI的状态管理有基本了解就够了。代码可以直接在新的工程里跑我会把需要配置的地方全部标注出来。2. 整体设计与思路拆解Slider和Progress在什么场景下怎么选2.1 Slider和Progress的本质区别交互控件与展示控件有一个问题经常被人搞混Slider和Progress都能表示“一段进度”那为什么不做成一个组件答案很简单——它们的职责完全不同。Slider是用来“改变值”Progress是用来“反映值”。一个是输入控件一个是输出控件。方向反了就出大问题。实际开发中最常见的错误是把Progress直接放在布局里然后通过定时器去改它的进度值模拟动画效果这本身没错。问题在于一旦把Slider和Progress用同一个状态驱动就很容易出现“用户拖一下滑块进度条跟着跳一下进度条动一下滑块又跟着抖一下”的“互相打架”现象。这个问题的根源是Slider的onChange事件和Progress的value绑定到同一个变量时进度条的更新触发了Slider的刷新回调又改回去形成了一个类似死循环的逻辑。正确思路是Slider负责“输入值”把值写入一个统一的状态变量Progress负责“展示值”从同一个状态变量读取。如果两者在同一页面已有唯一的取值来源应该是这个共享状态。但要注意如果目标是做一个播放进度条Slider的拖动行为和Progress的自动推进是不能混在一起的。我的做法是播放中Progress绑定播放器的当前位置用户一旦开始拖动Slider就暂停Progress的自动推进改用Slider临时值做预览拖完再统一提交。这个细节看似细微但直接影响手感。2.2 常见界面方案的取舍单选滑块、范围滑块与连续/离散模式在市面上常见的界面里Slider大致分三类用途单点调节、范围选择、展示型滑动不允许手调。ArkUI的Slider用blockColor、selectedColor、trackColor这几个属性控制视觉用min、max、step、value控制数值逻辑。范围选择需要额外开启minLabel、maxLabel之类的配置吗不需要ArkUI实现了独立的“范围Slider”模式看完第三部分会详细说。设计上要尽早确定用户到底需要连续调节还是离散调节。连续调节适合音量、亮度、缩放比例离散调节适合选择星级、选择档位、选择倍数。离散模式要配合step属性比如step{1}同时把min和max控制在整数范围滑块就会自动“一格一格地跳”。但是ArkUI的step并不是一个严格意义的“拒绝对非整数进行取值”的开关它更像是一个吸附效果。实际改值时你拿到的值还是可能出现浮点误差。解决办法是在事件回调里自己取整别指望框架给你保证精确值。另外有一种易错的情况拖拽过程中的实时更新。Sliders的onChange会连续回调如果你把这几次回调都拿去发请求或者更新数据表请求量会非常惊人。我的一般做法是onChange只更新本地UI状态比如预览文字、滑动位置在onChangeEnd或者释放事件的回调里才真正提交值。这个套路在后面的DEMO里会演示。2.3 从使用场景反推样式配置有人喜欢把Slider做成“又粗又圆”的可爱风格有人喜欢做成“细长条”的极简风格。技术讲Slider的轨道厚度可以通过trackThickness控制滑块大小由blockSize控制。样式上它是完全可定制的颜色、尺寸、轨道两端是否带圆角都能调。Progress同样有类型选择线性进度、环形进度、以及“胶囊进度”等等。用在哪取决于两个关键因素一是空间够不够二是进展是否可量化。空间小、只做一个“加载中”的提示环形小菊花最合适空间充裕、需要准确显示百分比线性进度条配文字最直观如果是上传图片墙这种每个item都要进度的每个item配一个小环形进度即可不要整页一个大进度条会极大增加用户的理解成本。与此同时要注意Progress只能“被展示”没有onChange之类的交互回调。如果某些场景需要用户点击进度条跳转位置就必须叠加Slider实现。很多播放器里“点击跳进度”的手势本质上是用Slider的点击事件做出来的。不要试图给Progress加手势。3. 核心细节解析Slider组件的参数、事件与实操要点3.1 Slider基础参数与API版本差异当前最新的API版本以API 12为例下Slider的常用参数我已经整理成一张速查表。建议对着你的IDE里的类型提示逐项核对因为不同API版本下老参数可能被标记为废弃但还能用新参数又可能只在特定版本生效。参数名类型作用备注valuenumber当前滑块值受控属性配合onChange使用minnumber最小值默认0maxnumber最大值默认100stepnumber步进值非整数时注意精度问题styleSliderStyle滑块风格包括OUTSIDE滑块在轨道外和INSIDE滑块在轨道内directionAxis排列方向默认Horizontal水平可选Vertical垂直trackThicknessLength轨道粗细设太大注意视觉平衡blockSizeLength滑块大小与trackThickness比值影响手感blockColorResourceColor滑块颜色支持Solid和渐变trackColorResourceColor未选中轨道颜色未经过部分selectedColorResourceColor选中轨道颜色已经过部分showTipsboolean是否显示气泡提示拖动时显示当前数值minLabel / maxLabelstring最小值/最大值标签显示在两端enabledboolean是否可交互false时变成展示态如果你还在用API 9或API 10有个容易踩坑的点range属性。旧版的Slider用range: [min, max]表示范围新版的min和max定义跟它不冲突但写法变了。实际开发里我遇到过从老项目迁移时只改了构造函数没改属性名导致滑块范围恒为0到100的诡异情况。排查方法很简单检查IDE有没有把旧属性名标成废弃。另外一个较少人注意但很重要的属性是selectedColor。很多新手把trackColor理解为“轨道颜色”结果运行时看到两侧轨道变成了同样颜色根本没有“选中/未选中”的区分。原因就是trackColor只对“未经过”的那一段生效已选中的一段需要用selectedColor单独配置。你自己设计主题时一定记得两个都要设否则视觉反馈会缺失。3.2 Slider的两种样式滑块在外面还是在里面ArkUI的Slider有两种style。SliderStyle.OUTSIDE是滑块凸出轨道看起来像一个小圆钮骑在轨道的上下边缘上SliderStyle.INSIDE是滑块在轨道内部整个视觉更紧凑。选择依据不只是“好看”还有使用场景。OUTSIDE样式适合手指较粗、需要大触摸区域的场景比如调节亮度INSIDE样式适合需要展示密度较高的列表里的行内调节比如设置页的“字体大小”行高有限滑块就不能太张扬。有很多人反馈INSIDE样式下滑块会部分被轨道背景吞掉尤其是当blockColor和trackColor颜色相近时。这不是bug是没拉开对比度。设计规范上滑块颜色和轨道未选中颜色要有至少30%的亮度差异否则用户会看不清当前滑块位置。没有设计稿时我习惯用“轨道深灰 滑块白色 选中蓝色”这个万能组合基本不会翻车。3.3 Slider事件处理onChange的时机与防抖Slider有三个核心事件onChange、onChangeEnd还有一个onClick点击轨道触发。其中用户最常见的问题是我明明没有拖动滑块只是点击了一下轨道为什么onChange也被连续调用了这个点在鸿蒙里确实容易让人疑惑。点击轨道默认行为是“跳转”——把滑块直接移动到点击位置同时触发一次onChange。如果你只是把onChange当成“正在拖动”的信号就会把一次点击误判为一次拖动。我的解决方案是使用一个“拖动中”标志位在Slider的onChange开始前会先触发onTouch事件如果touch类型是down就置为true表示按下。onChange回调里判断这个标志位为true才当作实时拖动处理。onChangeEnd里统一提交最终值之后把标志位置为false。这段逻辑看起来繁琐但能避免非常多的隐性问题。比如你在onChange里做了实时搜索那用户“点击一下轨道”就会触发几次搜索请求费流量还卡顿。做事严谨一点把“拖动中的即时反馈”和“结束后的最终提交”彻底拆开代码可维护性和体验都会好不少。3.4 实战细节Slider的实时预览值、步进精度与点击跳转如果要在滑块上方显示一个“当前值”的提示气泡可以利用showTips属性它会在拖动过程中自动显示数值气泡。但这个气泡的外观是系统默认的不能自定义里面的布局只能显示纯数字。如果你的需求是显示“50%”或者“已选第3档”这种额外文案就得自己做一个Text组件放在Slider旁边用状态变量驱动更新。步进精度问题也值得单独说一句。当step设为0.5时运行时可能拿到的值是4.999999999这种浮点数。在做文字展示时要先用Math.round(value / step) * step纠正再做显示。否则用户看到“4.99”会怀疑你的程序没写对。点击轨道跳转本身不需要额外代码组件默认支持。但如果要限制“只允许点击跳转不允许拖动”可以把enabled设为false再加一个透明Slider叠在底部通过点击事件修改value。这个方案经常用于实现“视频预览时不可拖动进度但可以点击快进/快退”的产品逻辑网上不少播放器就是这么干的。4. 核心细节解析Progress组件的类型、样式与进度控制4.1 Progress的基础类型线性、环形、胶囊ArkUI的Progress有三种默认外观ProgressType.Linear线性进度条、ProgressType.Ring环形进度、ProgressType.Capsule胶囊进度常见于加载后显示“完成”状态。定义Progress时需要通过构造函数传入ProgressOptions对象里面包含value、total和type三个核心参数。// 线性 Progress({ value: 40, total: 100, type: ProgressType.Linear }) // 环形 Progress({ value: 40, total: 100, type: ProgressType.Ring }) // 胶囊 Progress({ value: 40, total: 100, type: ProgressType.Capsule })value就是当前进度值total是总进度值两者相除就是百分比。需要注意如果你初始化时不设置value默认是0界面会停留在“空状态”如果你希望初始显示“满状态”value要设为和total相等。线性进度条默认是水平的类似一个扁长条。环形的默认直径可以通过width和height控制。胶囊进度两头是圆角视觉上更像一个小药丸适合用来做单个任务的加载反馈比如“保存文件”这类操作。4.2 环形Progress的样式细节strokeWidth和两端圆帽环形Progress在人机交互中最考验细节。关键参数有strokeWidth圆环粗细不是外圆宽高、backgroundColor底环颜色、foregroundColor进度环颜色、以及一些API 12新增的样式控制字段。有个很常见的坑很多人以为设置Progress的width和height可以改变圆环粗细结果发现外圈被放大了圆环本身细得可怜。原因是width和height控制的是整个圆的外接矩形而圆环粗细必须通过strokeWidth单独指定。strokeWidth越大环越粗反之越细。还有一个容易被忽略的样式点是“圆帽”。默认情况下环形Progress的进度条两端是平头但如果设计稿上要求两端是半圆圆帽就要在构造Progress时传入style参数配置strokeRound之类的属性。具体字段在API不同版本上略有区别我在API 12里用的写法是style: { strokeRound: true }这么设置。这个细节如果不调圆环看起来会有一种“断口生硬”的感觉对追求精致视觉的产品影响很大。在纯官网文档里“圆帽”通常放在了不起眼的分类里不仔细找根本发现不了。这里把它拎出来就是你设计循环进度时的第一个加分细节。4.3 Progress的动画让进度变化“顺滑”而不是“跳变”实际场景中进度条的数值基本不是硬切过去的。下载进度、上传进度、任务完成度都应该配上过渡动画——否则进度条会像心电图一样一跳一跳体验非常差。ArkUI中进度值的变化默认是即时生效的。要想实现平滑动画基本思路是用animateTo包裹value的变更。下面这段是我经常用的写法this.animationController animateTo({ duration: 300, curve: Curve.EaseOut }, () { this.currentProgress targetProgress })要注意一个问题animateTo是针对状态变量变更的动画容器它会让所有在闭包内变化的可动画属性都动起来。如果同一时刻页面里还有其他状态在变化可能会引发意外的“连带动画”。为了避免误伤我的做法是核心进度变量单独用一个对象管理其他非动画变量不要放在同一个闭包里变更。对于环形进度动画效果尤其明显。但animateTo对环形Progress生效有一个前提——Progress的style配置里必须支持动画修改如果你通过直接赋值的方式改value有时会有细微的“闪烁”现象。这通常是因为进度环在每一帧里被重新绘制了。遇到这种情况给Progress加一个固定的key或者在组件外再包一层通常能解决大部分问题。4.4 Progress的隐藏技巧用样式做“加载中菊花”用动态值做“未知进度”有一部分操作是“不知道总量的”比如等待服务器返回、刷新列表。这种场景不适合用确定进度应该用循环动画。ArkUI的Progress本身没有“未知进度”模式但我们可以通过状态更新让环形Progress循环旋转。具体做法不设置value让Progress只有底环再在外面套一个rotate动画让它无限旋转。这本质上就是一个“菊花”。代码结构如下State angle: number 0 // 在aboutToAppear中启动动画 animateTo({ duration: 800, iterations: -1, curve: Curve.Linear }, () { this.angle 360 }) // 在旋转节点上绑定rotate属性 .rotate({ angle: this.angle })这个方案比引入额外的加载组件要轻量得多在鸿蒙上不需要额外依赖性能也足够好。但要注意动画启动之后一定要在aboutToDisappear里调用animateTo的取消逻辑否则页面关了动画还在跑会造成隐性资源占用。如果使用的是Animator或animateTo的回调版本记得持有句柄并调用取消方法。4.5 Percent文字拼接与UI细节线性Progress本身不显示百分比文字需要自己加一个Text。常见布局是进度条占宽度70%文字占30%用Row排在一行。文字的位置有两种选择右侧固定显示“40%”和进度条内部居中显示“40%”浮在轨道中央。后者需要用到Stack布局把Progress和Text叠起来。文字颜色和背景要有足够对比度同时随着进度值变化文字位置可能被两端遮挡需要预留安全边距。我推荐右侧显示实现简单且不受进度长短影响。只有在做胶囊进度时才适合把文字叠在内部因为胶囊的宽度通常比较固定。百分比的计算建议统一封装一个方法private getPercentStr(value: number, total: number): string { if (total 0) return 0% const percent Math.min(100, Math.round(value / total * 100)) return percent % }注意一定做total 0的保护否则后台返回异常值时会出现除零页面直接白屏一片这个问题我在生产环境里遇到过不止一次。5. 实操过程与核心环节实现做一个模拟播放器控制面板5.1 项目准备与页面结构为了把Slider和Progress真正用起来我这次做一个“模拟播放器控制面板”的Demo。功能包括一首虚拟歌曲的播放进度Progress自动推进、播放进度的调节Slider手动拖动、音量调节第二个Slider以及播放/暂停按钮。新建一个工程后页面结构用Column和Row组合。顶部是一个环形Progress模拟“碟片旋转”的效果中间是歌曲信息底部是播放控制条和音量条。我先把关键的状态变量定义好Entry Component struct PlayerDemo { State isPlaying: boolean false State currentTime: number 0 // 当前播放秒数 State duration: number 240 // 总时长4分钟 State volume: number 60 // 音量0-100 State sliderTempValue: number 0 // 拖动中的临时进度值 private timerId: number -1 build() { // 页面内容 } }状态变量拆成了播放进度和音量两组因为它们的更新频率和交互语义完全不同。播放进度靠定时器推进音量靠用户滑块输入。两者如果共用一套状态拖动音量时进度也会跟着重置就会出现逻辑错乱。5.2 播放进度的自动推进与暂停处理我用定时器模拟播放器的推进逻辑。每秒钟更新一次currentTime并把Progress的value绑定到它。为了不阻塞UI定时器间隔设1000ms即可精度足够。private startTimer() { if (this.timerId 0) return this.timerId setInterval(() { if (this.currentTime this.duration) { this.stopTimer() this.isPlaying false return } this.currentTime }, 1000) } private stopTimer() { if (this.timerId 0) { clearInterval(this.timerId) this.timerId -1 } }播放和暂停按钮切换isPlaying在播放时调用startTimer暂停时调用stopTimer。注意处理边界如果播放到末尾时间停在总时长再按播放键时要先归零重来。播放Progress绑定当前值和总时长代码如下Progress({ value: this.currentTime, total: this.duration, type: ProgressType.Linear }) .width(100%) .height(8) .style({ strokeWidth: 8, strokeRound: false }) .backgroundColor(#e0e0e0) .foregroundColor(#007dff)这里没有做动画是因为定时器本身按秒推进如果再加animateTo反而会造成进度“追尾”。真实的播放器如果需要平滑推进可以把定时器缩短到200ms并用动画过渡变化值。但在这个Demo里秒级刷新已经能演示核心交互不会影响理解。5.3 Slider的拖动状态管理和值提交下面这段是整篇文章的重中之重Slider拖动时的状态分离。我用sliderTempValue存拖动过程的临时值用currentTime存真正播放位置。拖动开始时先暂停定时器拖动过程中只更新sliderTempValue让UI上的预览值跟随手指拖动结束时把sliderTempValue一次性提交到currentTime再恢复播放。代码如下Slider({ value: this.isDragging ? this.sliderTempValue : this.currentTime, min: 0, max: this.duration, step: 1 }) .layoutWeight(1) .onChange((value: number) { if (!this.isDragging) { this.isDragging true this.stopTimer() } this.sliderTempValue value }) .onChangeEnd((value: number) { this.isDragging false this.currentTime Math.round(value) if (this.isPlaying) { this.startTimer() } })这里有一个很关键的手感细节Slider的value不要直接绑定currentTime否则拖动过程中定时器一更新滑块就会被拽回去。用isDragging做开关UI表现会非常稳定。这就是前面提到的“防止打架”的具体实现。我用Math.round(value)解决step1时可能出现的浮点显示问题。onChangeEnd里提交时value已经被step吸附过但为了保险仍然取整再赋值。5.4 环形Progress模拟“播放中状态”顶部的环形Progress可以用来做播放状态的可视化反馈。播放时环形进度跟随播放位置转动暂停时环形进度停止没有播放任何内容时显示底环灰色内部放一个播放图标。代码实现上把环形Progress的value绑定currentTimetotal绑定duration同时加一个foregroundColor的颜色判断Progress({ value: this.currentTime, total: this.duration, type: ProgressType.Ring }) .width(120) .height(120) .style({ strokeWidth: 6, strokeRound: true, enableScanEffect: false }) .backgroundColor(#e0e0e0) .foregroundColor(this.isPlaying ? #007dff : #aaaaaa)enableScanEffect这个字段可以开启后让圆环带上扫描渐变的动态效果但因为我们有定时器推进不需要额外的扫描效果所以显式关闭。如果你想要更炫的视觉效果可以试着开启但要注意它和动画配合时性能开销会变大。5.5 音量Sliders的独立处理音量滑块跟播放进度滑块在同一页面但需要完全独立的状态。不要试图共用一个onChange逻辑那会把逻辑搞得很混乱。我单独绑定volume用onChange实时更新用onChangeEnd做收尾比如保存用户偏好。耳边很多教程里会把音量滑块也做成“拖动时存临时值结束时再提交”完全是多此一举。音量是要“即时反馈”的拖一下马上听到声音变化才有手感。这里的核心不是“尽量少更新”而是“不要让其他组件跟着变化”。因此它和进度Slider的分离逻辑并不相同区别一定要理解清楚。代码Slider({ value: this.volume, min: 0, max: 100, step: 1 }) .onChange((value: number) { this.volume Math.round(value) })5.6 完整页面组装最后用Column把各部分装起来核心是空间分配进度区在上控制区在下。给Control区加了背景色和圆角也让页面的层次更清晰。下面给出核心的build结构可以直接跑build() { Column({ space: 20 }) { // 环形进度模拟唱片 Stack({ alignContent: Alignment.Center }) { Progress({ value: this.currentTime, total: this.duration, type: ProgressType.Ring }) .width(120) .height(120) .style({ strokeWidth: 6, strokeRound: true, enableScanEffect: false }) .backgroundColor(#e0e0e0) .foregroundColor(this.isPlaying ? #007dff : #aaaaaa) Text(this.isPlaying ? 播放中 : 已暂停) .fontSize(14) .fontColor(#666666) } // 歌曲名和当前时间 Text(AI生成的一首Demo曲目) .fontSize(18) .fontWeight(FontWeight.Bold) // 线性进度条与时间标签 Row() { Text(this.formatTime(this.currentTime)) Slider({ ... }) Text(this.formatTime(this.duration)) } // 播放控制按钮 Row({ space: 20 }) { Button(this.isPlaying ? 暂停 : 播放) .onClick(() { if (this.isPlaying) { this.stopTimer() } else { if (this.currentTime this.duration) { this.currentTime 0 } this.startTimer() } this.isPlaying !this.isPlaying }) } // 音量条 Row() { Text(音量) Slider({ value: this.volume, min: 0, max: 100, step: 1 }) .layoutWeight(1) .onChange((value: number) { this.volume Math.round(value) }) Text(this.volume %) } } .width(100%) .padding(20) }运行页面后可以看到环形进度和线性进度同时显示同一首歌的播放进度拖动Slider会精准控制播放位置音量Slider独立工作。这个Demo麻雀虽小五脏俱全适合作为后续播放器页面的起点模板。6. 常见问题与排查技巧实录6.1 原生组件里Slider的滑块“太小”或“点不准”鸿蒙的Slider默认滑块尺寸偏小尤其在平板上操作时很难精准命中。解决方法是给Slider设置blockSize属性适当放大滑块。但注意blockSize并不是等比缩放整个滑块样式的如果你设置了自定义图标或图片它会以blockSize作为基准尺寸。手感层面还有一个细节Slider的触摸热区其实比可视滑块大这是系统默认行为。想要自定义热区大小可以在外层套一个.padding或者.gesture拦截更长的手势。我在做视频拖动进度条时会故意给Slider外层加一个高度30的容器让视觉条本身只有10最大化手指接触面积又不影响外观。6.2 进度条出现闪烁或白条环形Progress在动态更新时出现闪烁多半是圆环两端有“接缝”导致的。把strokeRound打开并且确保value和total都是整数闪烁概率会小很多。如果仍然闪烁试试不直接修改value而是用animateTo过渡让圆环不是逐帧跳变而是平滑旋转视觉效果会更稳。6.3 API版本差异旧代码迁移报错或属性失效我在升级工程时遇到过Slider不支持blockColor的情况排查后发现是API版本不够新。这种情况下查看SDK文档里有没有对应的兼容属性。API 10之前滑块颜色叫selectedColorAPI 11之后改成了blockColor。两个都是控制滑块主体颜色但命名的变更容易让老工程编译报错。最简单的方法是升级IDE到最新稳定版本然后把报错属性和文档对齐。不建议在代码里随机加ts-ignore会让问题隐藏得更深。6.4 Progress的百分比显示和实际逻辑值不一致这个问题通常来自浮点计算。当total不是100时比如总时长240秒当前时间50秒进度百分比是20.83%但页面上显示的进度条可能只有20%。答案是线性Progress本身不存在精度问题它只是一个比例计算。问题通常出在你把value错误传成了别的单位。比如value传了毫秒而total传了秒比例就会差1000倍。排查时先打印value和total对照单位再用计算器口算一下比例95%的问题都能秒定位。6.5 触摸手势冲突当Slider外层嵌套了Scroll或Swiper时上下滑动手势很容易被消费或穿透导致Slider拖不动。我的解决方案是给Slider加.gesture优先权使用GesturePriority.High。另外要检查父组件是否对相同方向的手势做了处理。如果水平Scroll包含水平Slider原则上是不建议的因为手势判定会互相干扰。如果业务非要这么排就只能在Slider的触摸回调里动态禁用父组件的滚动这种特殊场景可以自己封装一个容器组件来处理。7. 实操总结与扩展建议写到这里Slider和Progress的绝大多数使用场景基本覆盖了。我个人在实际开发里体会最深的一条是这类基础组件看着不起眼但把它们的状态管理和交互细节做到不打架才是用户觉得“这个App用起来顺手”的隐形功夫。如果你正在做播放器、下载管理、表单填写不妨直接把上面的Demo改造成自己的基础模块。最后再分享一个小技巧Slider和Progress都可以通过.universalTransition()做共享元素转场。比如列表页展示一个下载进度点击进入详情页时环形Progress可以平滑放大成详情页的大进度环。这个效果对页面层级转场非常加分。但要提醒一句共享元素转场要求两个页面都有对应的universalTransition标识并且组件类型一致否则转场会无效。调试这类问题时先确认两个组件尺寸、圆角、opacity是否都匹配再考虑动画曲线。下次写播放器或文件管理页面时建议直接尝试做一次完整的组合——播放进度Slider、缓冲进度Progress、列表下载进度多个胶囊Progress并用模拟一次真实的数据回填流程。把今天拆过的参数、事件、动画和坑都用上你对这两个组件的掌握程度基本就能超过大多数项目里的存量代码了。
阅读完成 · 觉得有帮助?