HarmonyOS listitem位置保持的问题

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

HarmonyOS
2天前
浏览
收藏 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)')
  }
}
//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);
    })
  }
}
//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>()
}
//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)
  }
}
分享
微博
QQ
微信
回复
2天前
相关问题
HarmonyOS listItem问题
287浏览 • 1回复 待解决
HarmonyOS ListItemswipeAction(end:)问题
20浏览 • 1回复 待解决
HarmonyOS CustomDialog位置问题
364浏览 • 1回复 待解决
HarmonyOS TextInput clearButton 位置问题
129浏览 • 1回复 待解决
HarmonyOS 系统位置服务问题
252浏览 • 1回复 待解决
HarmonyOS RichEditor光标位置异常问题
22浏览 • 1回复 待解决
HarmonyOS申请用户位置权限问题
537浏览 • 1回复 待解决
HarmonyOS 如何实现Listitem拖拽排序
59浏览 • 1回复 待解决
HarmonyOS 后台任务保持
319浏览 • 1回复 待解决
JS 获取控件位置并动态赋值问题
4043浏览 • 1回复 待解决
HarmonyOS 位置权限变更监听回调问题
101浏览 • 1回复 待解决
List及ListItem组件使用
1783浏览 • 1回复 待解决
HarmonyOS ListItem之间存在空隙
393浏览 • 1回复 待解决