最近项目中有个需求,关于滑动组件 Scroll 底部指示器跟随手势滑动等比例展示的效果,下图展示了要实现的效果。
自定义指示器组件
这里其实不是一个进度条,所以需要我们需要自定义绘制该组件,在鸿蒙中绘制组件单独使用,用于在页面上绘制指定的图形。有7种绘制类型,分别为Circle(圆形)、Ellipse(椭圆形)、Line(直线)、Polyline(折线)、Polygon(多边形)、Path(路径)、Rect(矩形),这里我么使用 Rect 进行矩形绘制,一个黑色的 Rect当做指示器的背景,蓝色当做指示器的进度。然后动态设置进度的左边距。
@Entry
@Component
export struct RectIndicator {
@Prop marginLeft: number = 0 //indicator距离进度距离左边间距,默认是 0
indicatorHeight: 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().xOffset
LogUtil.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。