HarmonyOS listitem位置保持的问题

竖向的list嵌套横向滑动的list,横向的list滑动之后再上下滑动怎么保持当前横向list的位置呢?list有属性可以设置吗?

HarmonyOS
2024-12-24 16:48:38
616浏览
收藏 0
回答 1
回答 1
按赞同
/
按时间
Heiang

请参考下面的demo:

//1.列表页ScrollListDemoPage
import { CommonDataSource } from './CommonDataSource'
import { LeftItemWidth, RightItemWidth, titleCount, pageSize, ListData, listItemData } from './utils'
import { ItemComponent } from './ItemComponent'

@Entry
@Component
struct ScrollListDemoPage {
  // 头部标题列表,每一列的标题
  private titleList: string[] = []
  // 列表数据--展示的内容
  @State listData: listItemData[] = []
  // 列表数据垂直滚动
  verticalScroller: Scroller = new Scroller()
  // 列表数据横向滚动
  horizontalScroller: Scroller = new Scroller()
  // 左侧名称列滚动
  leftScroller: Scroller = new Scroller()
  // 头部标题列滚动
  topScroller: Scroller = new Scroller()
  // 列表数据横向滚动的距离
  @State remainOffset: number = 0
  // 维护一个list控制器数组,用于保存所有横向列表的 ListScroller
  @State listScrollerArr: ListScroller[] = []
  // list的开始下标,用来刷新当前屏幕显示的列表区域
  @State startIndex: number = 0
  // list的结束下标
  @State endIndex: number = 0
  // 列表数据源
  private dataSource = new CommonDataSource<ListData>()
  // 头部标题列表数据源
  headerList = new CommonDataSource<string>()
  // 实际处理的数据
  showList: ListData[] = []
  // 用来模拟上拉加载更多页
  @State pageNum: number = pageNum
  aboutToAppear(): void {
    // 获取数据
    this.loadData()
  }

  loadData() {
    for (let i = 0; i < titleCount; i++) {
      this.titleList.push('标题' + i)
      let itemData: listItemData = {
        text: '内容' + i,
        id: i + this.getRandomVal(),
      }
      this.listData.push(itemData)
    }
    this.headerList.setData(this.titleList)

    for (let i = 0; i < pageSize; i++) {
      // 每次获取下一页数据时,需要同步增加list的控制器
      this.listScrollerArr.push(new ListScroller())

      let listItemData: ListData = new ListData()
      listItemData.fundName = '股票基金' + i
      listItemData.id = '股票基金' + i
      listItemData.textDataSource = new CommonDataSource<listItemData>()
      listItemData.textDataSource.setData(this.listData)
      this.showList.push(listItemData)
    }
    this.dataSource.setData(this.showList)
  }

  // 数据刷新
  refreshData() {
    let listData: listItemData[] = []
    for (let i = 0; i < titleCount; i++) {
      let text = '内容' + i + '-' + this.getRandomVal()
      let itemData: listItemData = {
        text,
        id: text
      }
      listData.push(itemData)
    }
    // 只刷新当前显示在屏幕的数据
    for (let i = this.startIndex; i <= this.endIndex; i++) {
      this.showList[i].textDataSource.setData(listData)
    }
    this.dataSource.refreshDataByIndex(this.startIndex, this.endIndex, this.showList.slice(this.startIndex, this.endIndex))
  }

  // 添加数据
  getNewListPage() {
    let listData: listItemData[] = []
    let showList: ListData[] = []
    for (let i = 0; i < titleCount; i++) {
      let text = '内容' + i + '-' + this.getRandomVal()
      let itemData: listItemData = {
        text,
        id: text
      }
      listData.push(itemData)
    }

    let s = pageSize * this.pageNum
    this.pageNum++
    let e = pageSize * this.pageNum
    for (let i = s; i < e; i++) {
      // 每次获取下一页数据时,需要同步增加list的控制器
      this.listScrollerArr.push(new ListScroller())

      let listItemData: ListData = new ListData()
      listItemData.fundName = '股票基金' + i
      listItemData.id = '股票基金' + i
      listItemData.textDataSource = new CommonDataSource<listItemData>()
      listItemData.textDataSource.setData(listData)
      showList.push(listItemData)
    }
    this.dataSource.pushDataArray(...showList)
  }

  // 生成1-100的随机数
  getRandomVal() {
    const min = 1;
    const max = 100;
    const randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
    return randomInt + ''
  }

  build() {
    Column() {
      // 头部标题
      this.titleBuilder()
      // 分割线
      Divider().strokeWidth("100%").color(0xeeeeee)

      Row() {
        // 左侧列
        this.leftBuilder()
        // 右侧列
        this.rightScroll()
      }
    }
    .height('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  titleBuilder() {
    Row() {
      Column() {
        Text('名称')
      }
      .width(LeftItemWidth)
      .height(48)
      .backgroundColor(Color.White)
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Start)
      .padding({ left: 16 })

      // 头部标题列表
      List({ scroller: this.topScroller }) {
        LazyForEach(this.headerList, (item: string, index: number) => {
          ListItem() {
            Text(item)
              .height(48)
              .width(RightItemWidth)
              .textAlign(TextAlign.Start)
              .padding({ left: 16 })
              .backgroundColor(0xFFFFFF)
          }
        }, (item: string) => item)
      }
      .listDirection(Axis.Horizontal)
      .edgeEffect(EdgeEffect.None)
      .scrollBar(BarState.Off)
      .width('calc(100% - 140vp)')
      .layoutWeight(1)
      .onScrollFrameBegin((offset: number, state: ScrollState) => {
        for (let i = this.startIndex; i <= this.endIndex; i++) {
          this.listScrollerArr[i].scrollTo({
            xOffset: this.topScroller.currentOffset().xOffset + offset,
            yOffset: 0,
            animation: false
          })
        }
        return { offsetRemain: offset }
      })
    }.height(48).width('100%').justifyContent(FlexAlign.Start)
  }

  @Builder
  leftBuilder() {
    List({ scroller: this.leftScroller }) {
      LazyForEach(this.dataSource, (item: ListData, index: number) => {
        ListItem() {
          Column() {
            Text(item.fundName)
              .height('100%')
              .backgroundColor(0xFFFFFF)
              .layoutWeight(1)
              .margin({ left: 16 })
            Divider().strokeWidth("100%").color(0xeeeeee)
          }
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Start)
        }
        .height(60)
      }, (item: ListData, index: number) => index + '')
    }
    .listDirection(Axis.Vertical)
    .scrollBar(BarState.Off)
    .edgeEffect(EdgeEffect.None)
    .height('calc(100% - 48vp)')
    .width(LeftItemWidth)
    .onScrollFrameBegin((offset: number, state: ScrollState) => {
      this.verticalScroller.scrollTo({
        xOffset: 0,
        yOffset: this.leftScroller.currentOffset().yOffset + offset,
        animation: false
      })
      return { offsetRemain: offset }
    })
  }

  @Builder
  rightScroll() {
    Scroll(this.horizontalScroller) {
      List({ initialIndex: 0, scroller: this.verticalScroller }) {
        LazyForEach(this.dataSource, (item: ListData, index: number) => {
          ListItem() {
            ItemComponent({
              scroller: this.listScrollerArr[index],
              remainOffset: this.remainOffset,
              data: item,
              scrollCallBack: (value) => {
                this.topScroller.scrollTo({ xOffset: value, yOffset: 0, animation: false })
                for (let i = this.startIndex; i <= this.endIndex; i++) {
                  if (i !== index) {
                    this.listScrollerArr[i].scrollTo({ xOffset: value, yOffset: 0, animation: false })
                  }
                }
              },
              remainOffsetCallBack: (value) => {
                this.remainOffset = value
              },
            })
          }
        })
      }
      .height('100%')
      .cachedCount(2)
      .flingSpeedLimit(1600)
      .listDirection(Axis.Vertical)
      .scrollBar(BarState.Off)
      .edgeEffect(EdgeEffect.None)
      .nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST })
      .onScrollFrameBegin((offset: number, state: ScrollState) => {
        this.leftScroller.scrollTo({
          xOffset: 0,
          yOffset: this.verticalScroller.currentOffset().yOffset + offset,
          animation: false
        })
        return { offsetRemain: offset }
      })
      .onScrollIndex((start: number, end: number) => {
        this.startIndex = start
        this.endIndex = end
        // 只滚动当前显示范围内的item
        for (let i = start; i <= end; i++) {
          this.listScrollerArr[i].scrollTo({ xOffset: this.remainOffset, yOffset: 0, animation: false })
        }
      })
      .onReachEnd(() => {
        setTimeout(() => {
          this.getNewListPage()
        }, 1000)
      })
      .onReachStart(() => {
        setTimeout(() => {
          this.refreshData()
        }, 1000)
      })
    }
    .position({ x: LeftItemWidth, y: 0 })
    .onScroll((scrollOffset: number, scrollState: ScrollState) => {
      this.topScroller.scrollTo({ xOffset: this.remainOffset, yOffset: 0, animation: false })
    })
    .scrollBar(BarState.Off)
    .edgeEffect(EdgeEffect.None)
    .scrollable(ScrollDirection.Horizontal)
    .backgroundColor(0xDCDCDC)
    .height('calc(100% - 48vp)')
    .width('calc(100% - 140vp)')
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
//2.数据源CommonDataSource.ets
export class CommonDataSource<T> implements IDataSource {
  private listeners: DataChangeListener[] = [];
  protected originDataArray: T[] = [];

  totalCount(): number {
    return this.originDataArray.length;
  }

  getAllData(): T[] {
    return this.originDataArray
  }

  getData(index: number) {
    return this.originDataArray[index];
  }

  addData(index: number, data: T): void {
    this.originDataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  pushData(data: T): void {
    this.originDataArray.push(data);
    this.notifyDataAdd(this.originDataArray.length - 1);
  }

  pushDataArray(...items: T[]): void {
    for (let data of items) {
      this.originDataArray.push(data);
      this.notifyDataAdd(this.originDataArray.length - 1);
    }
  }

  deleteDataUseContent(data: T): void {
    let delIndex: number = -1
    for (let index = 0; index < this.originDataArray.length; index++) {
      const element = this.originDataArray[index];
      if (data === element) {
        delIndex = index
      }
    }
    if (delIndex != -1) {
      this.deleteData(delIndex)
    }
  }

  deleteData(index: number): void {
    this.originDataArray.splice(index, 1);
    this.notifyDataDelete(index);
  }

  clear() {
    this.originDataArray.splice(0, this.originDataArray.length)
    this.listeners.forEach(listener => {
      listener.onDataDelete(0)
    })
  }

  setData(dataArray?: T[]) {
    if (dataArray) {
      this.originDataArray = dataArray
    } else {
      this.originDataArray = []
    }
    this.notifyDataReload()
  }

  refreshDataByIndex(start: number, end: number, dataArray: T[]) {
    this.originDataArray.splice(start, end - start, ...dataArray);
    this.notifyDataReload()
  }

  changeData(index: number, data: T): void {
    this.originDataArray.splice(index, 1, data);
    this.notifyDataChange(index);
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      this.listeners.splice(pos, 1);
    }
  }

  notifyDataReload() {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  notifyDataAdd(index: number) {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  notifyDataMove(from: number, to: number) {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }

  notifyDataDelete(index: number) {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  notifyDataChange(index: number) {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
//3.常量和一些自定义class utils.ets
import { CommonDataSource } from './CommonDataSource'

export const LeftItemWidth = 140
export const RightItemWidth = 120
// 标题列的个数
export const titleCount = 30
// 页数
export let pageNum = 1
// 每页的基金数量
export const pageSize = 20
// 基金的总数
export const totalCount = 400
@Observed
export class listItemData {
  text: string = ''
  id: string = ''
}
@Observed
export class ListData {
  id: string = ''
  fundName: string = ''
  textDataSource: CommonDataSource<listItemData> = new CommonDataSource<listItemData>()
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
//4.每一行都是 List 组件:ItemComponent.ets
import { RightItemWidth, listItemData, ListData } from './utils'

@Component
export struct ItemComponent {
  private scroller?: ListScroller = undefined
  private scrollCallBack?: (param: number) => void
  private remainOffsetCallBack?: (param: number) => void
  @ObjectLink data: ListData
  build() {
    Column() {
      List({ scroller: this.scroller }) {
        LazyForEach(this.data.textDataSource, (item: listItemData, index: number) => {
          ListItem() {
            Text(item.text)
              .height('100%')
              .width('100%')
              .textAlign(TextAlign.Start)
              .padding({left: 16})
              .backgroundColor(0xFFFFFF)
              .fontColor('#ffe72929')
              .maxLines(1)
              .textOverflow({overflow: TextOverflow.Ellipsis})
          }
          .width(RightItemWidth)
        }, (item: listItemData, index: number) => JSON.stringify(item) + index + '')
      }
      .cachedCount(4)
      .height('100%')
      .width('100%')
      .layoutWeight(1)
      .listDirection(Axis.Horizontal)
      .scrollBar(BarState.Off)
      .nestedScroll({
        scrollForward: NestedScrollMode.PARENT_FIRST,
        scrollBackward: NestedScrollMode.PARENT_FIRST
      })
      .edgeEffect(EdgeEffect.None)
      .onScroll(() => {
        if (this.remainOffsetCallBack) {
          this.remainOffsetCallBack(this.scroller!.currentOffset().xOffset)
        }
      })
      .onScrollFrameBegin((offset: number, state: ScrollState) => {
        if (this.scrollCallBack) {
          this.scrollCallBack(this.scroller!.currentOffset().xOffset + offset)
        }
        return { offsetRemain: offset }
      })
      Divider().strokeWidth("100%").color(0xeeeeee)
    }.height(60)
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
分享
微博
QQ
微信
回复
2024-12-24 18:58:07


相关问题
HarmonyOS ListItem位置指定问题
427浏览 • 1回复 待解决
HarmonyOS listItem问题
647浏览 • 1回复 待解决
HarmonyOS ListItemswipeAction(end:)问题
586浏览 • 1回复 待解决
HarmonyOS 关于ListItem生命周期问题
481浏览 • 1回复 待解决
HarmonyOS CustomDialog位置问题
896浏览 • 1回复 待解决
HarmonyOS Tabs位置问题
559浏览 • 1回复 待解决
HarmonyOS TextInput clearButton 位置问题
736浏览 • 1回复 待解决
HarmonyOS 系统位置服务问题
1076浏览 • 1回复 待解决
HarmonyOS RichEditor光标位置异常问题
943浏览 • 1回复 待解决
HarmonyOS申请用户位置权限问题
1299浏览 • 1回复 待解决
HarmonyOS 如何实现Listitem拖拽排序
999浏览 • 1回复 待解决
HarmonyOS 位置权限变更监听回调问题
955浏览 • 1回复 待解决
JS 获取控件位置并动态赋值问题
4657浏览 • 1回复 待解决
List及ListItem组件使用
2578浏览 • 1回复 待解决