在某些特定的场景下,需要实现滑动列表,唤起组件的功能,来提升人机交互的体验效果。如何实现该功能?
List组件作为官方提供的列表组件,与其它滚动组件一样具有位置监听功能,从而能实现滑动到指定位置唤起相关组件的功能。
- @Entry
- @Component
- struct ListRefreshLoad {
- listScroller: ListScroller = new ListScroller();
- @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- @State startOffset: number = 0;
- @State currentOffset: number = 0;
- @State listWidth: number = 0;
- @State allowOffset: number = 0;
- @State isShow: boolean = false;
-
- // 获取列表项的总长度
- getMaxWidth() {
- let widthAll = 0;
- for (let index = 0; index < this.arr.length; index++) {
- widthAll += 180;
- }
- widthAll -= this.listWidth;
- this.allowOffset = widthAll;
- }
-
- // 控制“更多”字体大小,
- getScale() {
- let scale = (this.currentOffset + this.startOffset - this.allowOffset - 30) / 30;
- if (scale > 1) {
- scale = 1;
- }
- return scale;
- }
-
- build() {
- Column() {
- Row() {
- List({ scroller: this.listScroller }) {
- ForEach(this.arr, (item: number) => {
- ListItem() {
- Row() {
- Text(item.toString());
- }
- .width(180)
- .height(80)
- .justifyContent(FlexAlign.Center)
- .alignItems(VerticalAlign.Center);
- };
- });
- }
- .scrollBar(BarState.Off)
- .height(80)
- .bindSheet($$this.isShow, this.myBuilder(), {
- height: 180
- })
- .onAreaChange((old, newVal) => {
- this.listWidth = Number(newVal.width);
- })
- .onScrollStart(() => {
- this.getMaxWidth();
- let offset = this.listScroller.currentOffset();
- this.startOffset = offset.xOffset;
- this.currentOffset = 0;
- this.isShow = false;
- })
- .onWillScroll((offset: number, state: ScrollState) => {
- this.currentOffset += offset;
- if (state === 2 && this.allowOffset + 60 < this.currentOffset + this.startOffset) {
- if (this.isShow) {
- return;
- }
- this.isShow = true;
- }
- })
- .listDirection(Axis.Horizontal);
-
- Column() {
- Text('更多')
- .fontSize(15);
- }
- .scale({ x: 0.7 + 0.3 * this.getScale(), y: 0.7 + 0.3 * this.getScale() })
- .justifyContent(FlexAlign.Center)
- .width(16)
- .height(80)
- .position({
- x: this.listWidth - 30 * this.getScale(),
- y: 0
- });
- };
- }
- .justifyContent(FlexAlign.Center)
- .width('100%')
- .height('100%');
- }
-
- @Builder
- myBuilder() {
- Column() {
- Text('弹窗')
- .margin({ top: 20 });
- }
- .width('100%')
- .height('100%');
- }
- }
实现效果如下:

- import { window } from '@kit.ArkUI';
-
- @Entry
- @Component
- struct Index {
- @State showTitle: boolean = false;
- @State msg: string[] = ['aa', 'bb', 'cc', 'dd', 'ee', 'ff', 'gg', 'hh', 'ii', 'jj'];
- private scrollerForList: Scroller = new Scroller();
- scrollGetData: number[] = [];
-
- aboutToAppear(): void {
- window.getLastWindow(this.getUIContext().getHostContext(), (err, data) => {
- data?.setWindowLayoutFullScreen(true); // 设置沉浸式布局
- });
- }
-
- build() {
- Stack({ alignContent: Alignment.Top }) {
- List({ space: 10, scroller: this.scrollerForList }) {
- ForEach(this.msg, (item: string) => {
- ListItem() {
- Text(`ListItem${item}`)
- .width('100%')
- .height('100%')
- .borderRadius(15)
- .fontSize(24)
- .textAlign(TextAlign.Center)
- .backgroundColor('#f1f3f5');
- }
- .width('90%')
- .height(100);
- }, (item: string) => item);
- }
- .alignListItem(ListItemAlign.Center)
- .width('100%')
- .edgeEffect(EdgeEffect.None)
- .onDidScroll((offset: number, State: ScrollState) => {
- if (offset < 0 && this.showTitle === true) {
- this.showTitle = false; // 下滑隐藏组件
- }
- if (offset > 0 && this.showTitle === false) {
- this.showTitle = true; // 上滑显示组件
- }
- console.info(`offset:${offset},State:${JSON.stringify(State)}`);
- });
-
- // 控制是否展示以下'开始组题'的UI组件
- if (this.showTitle) {
- Row() {
- Button('开始组题')
- .backgroundColor('#0A59F7');
- }
- .justifyContent(FlexAlign.Center)
- .width('80%')
- .height('8%')
- .position({ x: '10%', y: '90%' });
- }
- }
- .width('100%')
- .height('100%');
- }
- }
手指向上滑动同时内容向上,为上滑;手指向下滑动同时内容向下,为下滑。实现效果如下:

Q:采用偏移量监听List组件,滑动到边界时唤起组件显示异常?
A:以方案二为例,由于滑动组件默认有回弹模式,滑动到边缘时,回弹的偏移会导致showTitle变化,需要设置edgeEffect属性为None,或者增加滑动到边缘的控制条件,限制showTitle变化。
Q:半模态内嵌套Scroll组件如何实现下拉关闭功能?
A:在半模态组件中,监听滚动事件,如果是向下滑动且滑动距离超过一定阈值,则关闭半模态组件即可。