最近项目中有个需求,关于滑动组件 Scroll 底部指示器跟随手势滑动等比例展示的效果,下图展示了要实现的效果。
自定义指示器组件
这里其实不是一个进度条,所以需要我们需要自定义绘制该组件,在鸿蒙中绘制组件单独使用,用于在页面上绘制指定的图形。有7种绘制类型,分别为Circle(圆形)、Ellipse(椭圆形)、Line(直线)、Polyline(折线)、Polygon(多边形)、Path(路径)、Rect(矩形),这里我么使用 Rect 进行矩形绘制,一个黑色的 Rect当做指示器的背景,蓝色当做指示器的进度。然后动态设置进度的左边距。
@Entry
@Component
export struct RectIndicator {@Prop marginLeft: number = 0 //indicator距离进度距离左边间距,默认是 0indicatorHeight: number = 20 //indicator的高度indicatorWidth: number = 200 //indicator的背景宽度indicatorProgressWidth: number = 160 //indicator 的进度宽度build() {Stack() {//绘制矩形背景Rect({ width: this.indicatorWidth, height: this.indicatorHeight }).radius(this.indicatorHeight / 2).fill($r('app.color.bg_gray')).stroke(Color.Transparent)//绘制矩形进度Rect({ width: this.indicatorProgressWidth, height: this.indicatorHeight }).radius(this.indicatorHeight / 2).fill($r('app.color.main_color')).margin({ left: this.marginLeft }).stroke(Color.Transparent)}.alignContent(Alignment.Start)}
}
添加 Scroll 监听
新建一个 RectIndicator 类,需要把两个矩形层叠在一起,所以外层使用Stack布局进行嵌套。调用该自定义组件的时候,可以设置 组件的宽度(indicatorWidth) ,高度(indicatorHeight),进度的宽度(indicatorProgressWidth)以及动态设置进度的左边距(marginLeft),可以通过监听 Scroll 的滑动事件来动态设置 marginLeft。
Scroll(this.scroll) {Row() {ForEach(SUB_MENUS, (item: Menu, index) => {Column() {Image(item.menuIcon).width(28).height(28)Text(item.menuText).fontSize(12).fontColor($r("app.color.text_two")).margin({ top: 5 })}.width("20%").id("item")})}}.scrollable(ScrollDirection.Horizontal).margin({ top: 12 }).scrollBar(BarState.Off)//滑动监听.onDidScroll((_xOffset, _yOffset, scrollState) => {//当前状态为滑动状态if (scrollState == ScrollState.Scroll) {//获取滚动的偏移量,通过控制器获取比较准确const currentOffsetX = this.scroll.currentOffset().xOffsetLogUtil.debug("滑动偏移量", vp2px(currentOffsetX).toString())//子组件宽度*2=未显示出来的组件/指示器灰色部分let ratio = this.itemWidth * 2 / 10//指示器进度的偏移量=scroll 的偏移量/比率this.indicatorLeft = vp2px(currentOffsetX) / ratio}})
onDidScroll 可以对滑动组件(包括但不限于 Scroll)设置监听,这里判断 scrollState 为滑动状态,获取当前滑动的偏移量 currentOffsetX,通过Scroll 的偏移量计算出 指示器的左间距(indicatorLeft)。
使用自定义RectIndicator组件
RecIndicator({marginLeft: this.indicatorLeft, //左间距indicatorHeight: 4, //指示器的高度indicatorWidth: 30, //指示器的背景宽度indicatorProgressWidth: 20 //指示器进度的宽度}).margin({ top: 8, bottom: 8 })
使用方法:调用RecIndicator自定义组件,将高度,宽度等相关参数传递组件内,这里的进度宽度,可以通过 Scroll 组件长度计算出来,这里我就这只给一个宽度,不影响使用。
tips:indicatorLeft需要加一个@State 注解,保证组件可以根据indicatorLeft来实时刷新 UI。