鸿蒙 Harmony 开发 Ark Stage模式下 List 和Scroll实现锁定 双向表头的复杂表格。

效果图

设计图如上。

代码如下Monitor.etf:

关键代码:在左侧list上下滑动的时候,调用:if (this.isLeftScroll) { this.rightYScroller.scrollTo({ xOffset: 0,yOffset: this.verticalOffset}) }

右侧List上下滑动的时候,调用:if (!this.isLeftScroll) { this.leftYScroller.scrollTo({ xOffset: 0,yOffset: this.verticalOffset}) }

已知问题:1,右侧滑动带动左侧上下滑动的时候流畅,但是左侧带动右侧滑动的时候,右侧数据栏List出现卡顿和闪烁,希望能看出问题的不吝指教。

2,横屏适配没处理。

import util from '@ohos.util';
import CommonConstants from '../../common/constants/CommonConstants';
import Logger from '../../common/utils/Logger';
import { MonitorNameCodeData } from '../../model/MonitorNameCodeData';
import { MonitorRightData } from '../../model/MonitorRightData';
import {MonitorNameCodeDataSource, MonitorRightListDataSource} from '../../model/MonitorModel'
import { NameCodeItem } from './MonitorNameCodeItem';
import display from '@ohos.display';
import { MonitorListItem } from './MonitorListItem';
import WindowModel from '../../model/WindowModel';
import { getMonitorPageHeight } from './MonitorPage';

/*
 * 高度是全屏除去状态栏,底部导航栏,自选页Header(按钮区)
 * */
export function getScreenWidthVp():number{
  let disp=display.getDefaultDisplaySync();
  let vpW:number=px2vp(disp.width);
  return vpW;
}
export function getScreenHeightVp():number{
  let disp=display.getDefaultDisplaySync();
  let vpH:number=px2vp(disp.height);
  return vpH;
}
getStatusBarHeight():number{
  return 40;//此处省略从系统获取状态栏高度方法
}
export function getMonitorPageHeight():number{
 return getScreenHeightVp()-getStatusBarHeight()-CommonConstants.TAB_BAR_HEIGHT_PORT;
}
export function getMonitorListHeight():number{
  let listH:number=getMonitorPageHeight()-CommonConstants.MONITOR_HEADER_HEIGHT-CommonConstants.MONITOR_TITLE_HEIGHT;
  Logger.info("compute monitor right list height:"+listH);
  return listH;
}
@Component
export  struct Monitor {
  @State colWidth:number=0;
  @State monitorTits:Resource[]=[$r('app.string.DN_LTRADE'),$r('app.string.DN_CHP_S'),$r('app.string.DN_CH_S'),
  $r('app.string.DN_BID'),$r('app.string.DN_ASK'),$r('app.string.DN_HIGH'),$r('app.string.DN_LOW'),
  $r('app.string.DN_OPRICE'),$r('app.string.DN_PCLOSE'), $r('app.string.DN_VOL'),$r('app.string.DN_TOVER')
  ];//total 24 lines
  @Provide monitorNameCodeListener: MonitorNameCodeDataSource = new MonitorNameCodeDataSource();
  @Provide monitorRightDataList: MonitorRightListDataSource = new MonitorRightListDataSource();
  private leftYScroller: Scroller = new Scroller();
  private rightYScroller: Scroller = new Scroller();
  private verticalOffset:number=0;
  private horizonOffset:number=0;
  private isLeftScroll=false;
  build() {
    Row() {
      Column(){//top left corner and name code column
        Text("TopLeft")
          .width(100))
          .height(CommonConstants.MONITOR_TITLE_HEIGHT)
          .backgroundColor(Color.Gray)
        List({scroller: this.leftYScroller}){
          LazyForEach(this.monitorNameCodeListener, (item: MonitorNameCodeData) => {
            ListItem() {
              NameCodeItem({ name: item.name, code: item.code })
                .width(100))
                .height(50)
                .backgroundColor(Color.Yellow)
                .borderWidth({ right: 0.5 })
            } //ListItem
          })//LazyForEach
        }//List,如果子组件主轴方向总尺寸超过List父组件尺寸时,List主轴方向尺寸适应List的父组件尺寸。
        .height(getMonitorListHeight())
        .divider({ strokeWidth: 0.5, color: Color.Gray, startMargin: 5, endMargin: 5 })
        .onTouch((event?: TouchEvent) => {
          if (event === undefined) {
            return;
          }
          switch (event.type) {
            case TouchType.Down:
              this.isLeftScroll=true;
              break;
            case TouchType.Up:
              break;
            case TouchType.Move:
              break;
          }
        })
        .onScroll((scrollOffset: number, scrollState: ScrollState) => {
          this.verticalOffset = this.leftYScroller.currentOffset().yOffset;
          this.horizonOffset=this.leftYScroller.currentOffset().xOffset;
          if (this.isLeftScroll) {
            this.rightYScroller.scrollTo({ xOffset: 0,yOffset: this.verticalOffset})
          }
        })
      }//左侧名字代码列
      .width(100)
      .height('100%')
      .justifyContent(FlexAlign.Start)
      .backgroundColor(Color.Brown)
      Scroll() {//right title and right data area
        Column() {
          List(){//title
            ForEach(this.monitorTits,(item:string) => {
              ListItem(){
                Text(item)
                  .fontSize(18)
                  .width(70)
                  .height(CommonConstants.FULL_PARENT)
                  .backgroundColor(Color.Gray)
                  .textAlign(TextAlign.Center)
              }
            })
          }//list title
          .listDirection(Axis.Horizontal)
          .height(CommonConstants.MONITOR_TITLE_HEIGHT)
          List({scroller: this.rightYScroller}) {
            LazyForEach(this.monitorRightDataList, (item: MonitorRightData) => {
              ListItem() {
                MonitorListItem({rightItem:item})
              } //ListItem
            })
          }
          .width(this.monitorTits.length * 70)
          .height(getMonitorListHeight())
          .divider({ strokeWidth: 0.5, color: Color.Gray, startMargin: 5, endMargin: 5 })
          .onTouch((event?: TouchEvent) => {
            if (event === undefined) {
              return;
            }
            switch (event.type) {
              case TouchType.Down:
                this.isLeftScroll=false;
                break;
              case TouchType.Up:
                break;
              case TouchType.Move:
                break;
            }
          })
          .onScroll((scrollOffset: number, scrollState: ScrollState) => {
            this.verticalOffset = this.rightYScroller.currentOffset().yOffset;
            if (!this.isLeftScroll) {
              this.leftYScroller.scrollTo({ xOffset: 0,yOffset: this.verticalOffset})
            }
          })
        }
        .height('100%')
      }//右侧,标题和数据列
      .width(WindowModel.getInstance().getScreenWidthVp()-100)
      .height('100%')
      .scrollable(ScrollDirection.Horizontal)
    }//row
    .alignItems(VerticalAlign.Top)//子组件在Row内部的排列方式
    .width('100%')
    .height(getMonitorPageHeight()-CommonConstants.MONITOR_HEADER_HEIGHT)
    .backgroundColor('#00ff00')
  }
}

MonitorRightData.ts

import { MonitorData } from './MonitorData';
export class MonitorRightData  extends MonitorData{
  rightDatas:string[]=["29999.99","+199","0.67%","29999.00","30000","30100","29700.00","29699.59","29899.00","12345","123456789"];
  constructor() {
    super();
  }
}
export const rightDatas: MonitorRightData[]=[
  new MonitorRightData(),
  new MonitorRightData(),
  new MonitorRightData(),
  new MonitorRightData(),
  new MonitorRightData()
];

MonitorListItem.ets

import CommonConstants from '../../common/constants/CommonConstants'
import { MonitorRightData } from '../../model/MonitorRightData';
@Component
export struct MonitorListItem{
  private rightItem:MonitorRightData=new MonitorRightData();
  build(){
    Row() {
      ForEach(this.rightItem.rightDatas, (itemRight: string) => {
        Text(itemRight)
          .width(70)
          .height(CommonConstants.FULL_PARENT)
          .backgroundColor(Color.White)
          .textAlign(TextAlign.End)
          .maxLines(1)
          .borderWidth({ right: 0.5 })
          .borderColor(Color.Gray)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
      })
    } //Row
    .width(CommonConstants.FULL_PARENT)
    .height(50)
    .backgroundColor(Color.Brown)
  }
}

MonitorNameCodeItem.ets

import CommonConstants from '../../common/constants/CommonConstants';
@Component
export struct NameCodeItem{
  @State name:string="";
  @State code:string="";
  build() {
    Column() {
      Text(this.name)
        .width(CommonConstants.FULL_PARENT)
        .layoutWeight(5)
        .fontColor(Color.Brown)
        .backgroundColor(Color.White)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .maxLines(1)
      Text(this.code).fontSize(14)
        .width(CommonConstants.FULL_PARENT)
        .layoutWeight(3)
        .fontColor(Color.Black)
        .backgroundColor(Color.White)
        .maxLines(1)
    } //name and code col fistcol
    .alignItems(HorizontalAlign.Start)
    .justifyContent(FlexAlign.Start)
  }
}

MonitorModel.ts

// Basic implementation of IDataSource to handle data listener
import CommonConstants from '../common/constants/CommonConstants';
import { MonitorData } from './MonitorData';
import { nameCodeDatas,MonitorNameCodeData } from './MonitorNameCodeData';
import { rightDatas,MonitorRightData } from './MonitorRightData'

const createNameCodeRange = (): MonitorNameCodeData[] => {
  let result = new Array<MonitorNameCodeData>();
  for (let i = 0; i < 4; i++) {
    result = result.concat(nameCodeDatas);
  }
  return result;
}
const createRightListRange = (): MonitorRightData[] => {
  let result = new Array<MonitorRightData>();
  for (let i = 0; i < 4; i++) {
    result = result.concat(rightDatas);
  }
  return result;
}
// @ts-ignore
class BasicDataSource implements IDataSource {
  // @ts-ignore
  private listeners: DataChangeListener[] = [];
  private originDataArray: MonitorData[] = [];

  public totalCount(): number {
    return 0;
  }

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

  // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
  // @ts-ignore
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
  // @ts-ignore
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知LazyForEach组件需要重载所有子组件
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知LazyForEach组件需要在index对应索引处添加子组件
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知LazyForEach组件需要在index对应索引处删除该子组件
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }
}

export class MonitorNameCodeDataSource extends BasicDataSource {
  private listData = createNameCodeRange();

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

  public getData(index: number): MonitorData {
    return this.listData[index];
  }

  public pushData(): void {
    if (this.listData.length < CommonConstants.MONITOR_MAX_LENGTH) {
      // this.listData = [...this.listData, ...goodsInitialList];
      this.listData.concat(nameCodeDatas);
      this.notifyDataAdd(this.listData.length - 1);
    }
  }
}
export class MonitorRightListDataSource extends BasicDataSource {
  private listData = createRightListRange();

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

  public getData(index: number): MonitorRightData {
    return this.listData[index];
  }

  public pushData(): void {
    if (this.listData.length < CommonConstants.MONITOR_MAX_LENGTH) {
      // this.listData = [...this.listData, ...goodsInitialList];
      this.listData.concat(rightDatas);
      this.notifyDataAdd(this.listData.length - 1);
    }
  }
}

CommonConstants.ts

export default class CommonConstants {
static readonly FULL_PARENT = '100%';
static readonly MONITOR_MAX_LENGTH = 100;
static readonly TAB_BAR_HEIGHT_PORT=56;//系统默认值为56vp
static readonly MONITOR_HEADER_HEIGHT=50;
static readonly MONITOR_TITLE_HEIGHT=50;
}

参考

​​​​​鸿蒙两个Scroll互相监听彼此的滑动-卡顿问题_坚果的博客的博客-CSDN博客

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值