HarmonyOS6 - 鸿蒙双向滚动课程表案例

HarmonyOS6 - 鸿蒙双向滚动课程表案例

开发环境为:

开发工具:DevEco Studio 6.0.1 Release

API版本是:API21

本文所有代码都已使用模拟器测试成功!

1. 效果

2. 分析

开发思路如下:

  1. 页面分为两部分,左侧展示课程表节数和上课时间,右侧展示星期数和课程数据列表。
  2. 右侧的课程数据使用双层Scroll来实现数据的左右滑动。
  3. 为了达到滑动左侧课程表时间部分和上部星期数,对应的数据也同步滑动的效果,在onScrollFrameBegin回调中设置列表的垂直滚动控制器和水平滚动控制器;反之同理。
  4. onAppear函数中进行初始设置,使Scroller定位到当前星期数

3. 开发

基于以上开发思路,编码如下;

js 复制代码
import { CommonModifier } from '@kit.ArkUI';

export class Course {
  public name: ResourceStr = '-'
  public backColor: ResourceColor = '#EDC7FF'

  constructor(name: ResourceStr, backgroundColor: ResourceColor) {
    this.name = name;
    this.backColor = backgroundColor;
  }
}

export const COURSE_MODEL: Course[] = [new Course('-', Color.White),
  new Course('B1478 移动应用开发综合实训   1-18周 2C602', '#EDC7FF'),
  new Course('B2857 软件工程   1、3、5、7、9、11、13、15、17周 1509', '#FFE1E8'),
  new Course('B1469 Java Web框架技术   1-18周 1504', '#E1F9FF'),
  new Course('B4037 软件项目管理   1、3、5、7、9、11、13、15、17周 2C304', '#E5F0E1'),
  new Course('B3873 C语言基础   1-8周 3408', '#FFDACC'), new Course('B0383 大学生就业指导   1-8周 3406', '#ECFC9A'),
  new Course('B2857 软件工程   1-18周 1411', '#FFE1E8'),
  new Course('B1678 网络管理与维护   2、4、6、8、10、12、14、16、18周 1604', '#FFFCA3'),
  new Course('B1678 Python入门与实践   1-18周 1510', '#FBF0FF'),
  new Course('B1427 统一建模语言   1-18周 1509', '#FFE4C7'),
  new Course('B4037 软件项目管理   1-18周 2C304', '#FFF0F0'),
  new Course('B2857 软件工程综合实训   11-12周 1502', '#ECDC96')]

let preResourceId = 0;

@Entry
@Component
struct CourseTablePage {
  @State classIndex: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8']
  @State classTime: Array<string> = ['8:30-9:15', '9:25-10:10', '10:30-11:15', '11:15-12:10',
    '14:10-14:55', '15:05-15:45', '15:55-16:30', '16:45-17:00']
  weekDay: number = new Date().getDay()
  classScroller = new Scroller();
  timeScroller = new Scroller();
  weekdaysScroller = new Scroller();
  horizontalScroller = new Scroller();
  verticalScroller = new Scroller();
  data: Course[][] = [
    [COURSE_MODEL[0], COURSE_MODEL[1], COURSE_MODEL[4], COURSE_MODEL[1],
      COURSE_MODEL[10], COURSE_MODEL[2], COURSE_MODEL[0]],
    [COURSE_MODEL[3], COURSE_MODEL[1], COURSE_MODEL[4], COURSE_MODEL[1],
      COURSE_MODEL[10], COURSE_MODEL[2], COURSE_MODEL[0]],
    [COURSE_MODEL[5], COURSE_MODEL[8], COURSE_MODEL[9], COURSE_MODEL[0],
      COURSE_MODEL[0], COURSE_MODEL[7], COURSE_MODEL[10]],
    [COURSE_MODEL[5], COURSE_MODEL[8], COURSE_MODEL[9], COURSE_MODEL[0],
      COURSE_MODEL[0], COURSE_MODEL[7], COURSE_MODEL[10]],
    [COURSE_MODEL[11], COURSE_MODEL[1], COURSE_MODEL[2], COURSE_MODEL[0],
      COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[0]],
    [COURSE_MODEL[11], COURSE_MODEL[12], COURSE_MODEL[2], COURSE_MODEL[12],
      COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[0]],
    [COURSE_MODEL[11], COURSE_MODEL[12], COURSE_MODEL[2], COURSE_MODEL[12],
      COURSE_MODEL[4], COURSE_MODEL[5], COURSE_MODEL[0]],
    [COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[6], COURSE_MODEL[1],
      COURSE_MODEL[4], COURSE_MODEL[5], COURSE_MODEL[6]]
  ]
  arr: number[] = []
  classificationNames: Array<ResourceStr> = [
    '星期一',
    '星期二',
    '星期三',
    '星期四',
    '星期五',
    '星期六',
    '星期天']
  @State currentIndex: number = 2;
  @State tabBarModifier: CommonModifier = new CommonModifier();

  aboutToAppear() {
    for (let index = 0; index < this.classIndex.length; index++) {
      this.arr.push(index)
    }
    this.tabBarModifier.alignRules({})
  }

  @Builder
  itemBuilder(msg: ResourceStr, curHeight: Length = 100, curWidth: Length = 120,
    curBackgroundColor: ResourceColor = Color.White, curFontColor: ResourceColor = Color.Black) {
    Row() {
      Column() {
        Stack() {
          Column() {
          }
          .backgroundColor(curBackgroundColor)
          .width('100%')
          .height('100%')
          .opacity(0.7)

          Text(msg)
            .fontSize(12)
            .fontWeight(400)
            .textAlign(TextAlign.Center)
            .fontColor(curFontColor)
            .opacity(0.9)
            .width('100%')
            .height('100%')
            .padding(6)
        }
      }
      .padding(6)
      .justifyContent(FlexAlign.Center)
      .width(curWidth)
      .height(curHeight)
      .backgroundColor(Color.White)
    }
  }

  @Builder
  tabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
    Column() {
      Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
        .size({ width: 25, height: 25 })
      Text(title)
        .fontColor(this.currentIndex === targetIndex ? '#0A59F7' : '#60000000')
        .fontSize(10)
        .fontWeight(500)
        .margin({ top: 4 })
    }
    .height('100%')
    .margin({ top: 8 })
    .width('auto')
    .alignItems(HorizontalAlign.Center)
  }

  build() {
    Column() {
      Column() {
        Row() {
          Text('第23周')
            .fontSize(22)
            .fontWeight(800)
            .lineHeight(27)
        }
        .margin({ left: 32, bottom: 8, top: 12 })
        .height(42)
        .width('100%')
        .justifyContent(FlexAlign.Start)

        Column() {
          Row() {
            Column() {
              Text('节')
                .fontSize(14)
                .fontWeight(400)
                .textAlign(TextAlign.Center)
                .fontColor(Color.Black)
                .width('100%')
                .height('100%')
            }
            .justifyContent(FlexAlign.Center)
            .width(50)
            .height(42)

            Column() {
              Text('上课时间')
                .fontSize(14)
                .fontWeight(400)
                .textAlign(TextAlign.Center)
                .fontColor(Color.Black)
                .width('100%')
                .height('100%')
            }
            .justifyContent(FlexAlign.Center)
            .width(60)
            .height(42)

            Column() {
              Scroll(this.weekdaysScroller) {
                List() {
                  ForEach(this.classificationNames, (item: Resource, index: number) => {
                    ListItem() {
                      Row() {
                        Column() {
                          Text(item)
                            .fontSize(14)
                            .fontWeight(400)
                            .textAlign(TextAlign.Center)
                            .fontColor(Color.Black)
                            .width('100%')
                        }
                        .padding(4)
                        .justifyContent(FlexAlign.Center)
                        .width(120)
                        .height(42)
                        .backgroundColor(Color.White)
                      }
                    }
                  }, (item: string) => item + new Date().toString())
                }
                .listDirection(Axis.Horizontal)
                .edgeEffect(EdgeEffect.None)
              }
              .scrollable(ScrollDirection.Horizontal)
              .scrollBar(BarState.Off)
              .height(42)
              .onScrollFrameBegin((offset: number) => {
                this.horizontalScroller.scrollBy(offset, 0);
                return { offsetRemain: offset };
              })
            }
            .width('70%')
          }
          .width('100%')
          .position({ x: 0, y: 0 })
          .backgroundColor(Color.White)
          .borderRadius({ topLeft: 16 })
          .shadow({ radius: 50, color: '#25000000' })
          .zIndex(10)


          Row() {
            Column() {
              Scroll(this.classScroller) {
                Column() {
                  List({ space: 0, initialIndex: 0 }) {
                    ForEach(this.classIndex, (item: string, index: number) => {
                      ListItem() {
                        Row() {
                          Column() {
                            Text(item)
                              .fontSize(14)
                              .fontWeight(400)
                              .textAlign(TextAlign.Center)
                              .fontColor(Color.Black)
                              .width('100%')
                          }
                          .padding(4)
                          .justifyContent(FlexAlign.Center)
                          .width(50)
                          .height(100)
                          .backgroundColor(Color.White)
                        }
                      }
                    }, (item: string) => JSON.stringify(item))
                  }
                  .listDirection(Axis.Vertical)
                  .edgeEffect(EdgeEffect.None) // 滑动到边缘无效果
                }
              }
              .position({ x: 0, y: 0 })
              .width(50)
              .backgroundColor('#F8F8FF')
              .scrollBar(BarState.Off)
              .onScrollFrameBegin((offset: number) => {
                this.verticalScroller.scrollBy(0, offset);
                this.timeScroller.scrollBy(0, offset);
                return { offsetRemain: offset };
              })

              Scroll(this.timeScroller) {
                Column() {
                  List({ space: 0, initialIndex: 0 }) {
                    ForEach(this.classTime, (item: string, index: number) => {
                      ListItem() {
                        Row() {
                          Column() {
                            Text(item.split('-')[0] + ' -')
                              .fontSize(12)
                              .textAlign(TextAlign.Center)
                              .fontColor(Color.Black)
                              .width('100%')
                              .height('50%')
                              .align(Alignment.Bottom)
                              .lineHeight(20)
                            Text(item.split('-')[1])
                              .fontSize(12)
                              .textAlign(TextAlign.Center)
                              .fontColor(Color.Black)
                              .width('100%')
                              .height('50%')
                              .align(Alignment.Top)
                              .lineHeight(20)
                          }
                          .padding(4)
                          .justifyContent(FlexAlign.Center)
                          .width(60)
                          .height(100)
                          .backgroundColor(Color.White)
                        }
                      }
                    }, (item: string) => JSON.stringify(item))
                  }
                  .listDirection(Axis.Vertical)
                  .edgeEffect(EdgeEffect.None)
                }
              }
              .position({ x: 50, y: 0 })
              .width(60)
              .backgroundColor('#F8F8FF')
              .scrollBar(BarState.Off)
              .onScrollFrameBegin((offset: number) => {
                this.verticalScroller.scrollBy(0, offset);
                this.classScroller.scrollBy(0, offset);
                return { offsetRemain: offset };
              })
            }
            .width(110)
            .height('100%')
            .backgroundColor(Color.White)
            .shadow({ radius: 45, color: '#25000000', offsetY: -15 })
            .padding({ top: 4, bottom: 20 })

            Column() {
              Scroll(this.horizontalScroller) {
                Scroll(this.verticalScroller) {
                  Column() {
                    ForEach(this.arr, (_temp: number, index: number) => {
                      Row() {
                        ForEach(this.data[this.arr[_temp]], (item: Course) => {
                          this.itemBuilder(item.name, 100, 120, item.backColor, Color.Black)
                        }, (item: Course) => getResourceID().toString())
                      }
                    }, (_temp: number) => _temp + new Date().toString())
                  }
                }
                .scrollBar(BarState.Off)
                .scrollable(ScrollDirection.Vertical)
                .height('100%')
                .width(this.classificationNames.length * 120)
                .onScrollFrameBegin((offset: number) => {
                  this.classScroller.scrollBy(0, offset);
                  this.timeScroller.scrollBy(0, offset)
                  return { offsetRemain: offset };
                })
              }
              .padding({
                top: 4,
                left: 4,
                right: 4,
                bottom: 20
              })
              .scrollBar(BarState.Off)
              .scrollable(ScrollDirection.Horizontal)
              .onScrollFrameBegin((offset: number) => {
                this.weekdaysScroller.scrollBy(offset, 0);
                return { offsetRemain: offset };
              })
            }
            .zIndex(-100)
            .width('70%')
          }
          .position({ x: 0, y: 42 })
          .width('100%')
          .height('100%')
          .backgroundColor(Color.White)
        }
        .height('88%')
        .onAppear(() => {
          if (this.weekDay > 1) {
            this.weekdaysScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })
            this.horizontalScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })
          }
        })
        .width('100%')

      }
      .height('100%')
    }
    .width('100%')
    .height('100%')
  }
}

function getResourceID(): string {
  preResourceId++;
  return preResourceId.toString()
}

页面拷贝过去直接可以使用预览器观看效果

最后

  • 希望本文对你有所帮助!
  • 本人如有任何错误或不当之处,请留言指出,谢谢!
相关推荐
小学生波波1 天前
HarmonyOS6 - 鸿蒙CustomDialog封装信息提示框
arkts·鸿蒙·鸿蒙开发·harmonyos6·信息提示框
小学生波波1 天前
HarmonyOS - 鸿蒙开发百度地图案例
地图·百度地图·路线规划·鸿蒙开发·harmonyos6·鸿蒙地图·打点
小学生波波1 天前
HarmonyOS6 - 鸿蒙电商页面实战案例
登录页面·arkts·鸿蒙系统·电商·harmonyos6
迪霸戈1 天前
MyBatis动态SQL避坑:为什么List用`[0]`而不是`get(0)`
sql·list·mybatis
txinyu的博客2 天前
list 三个经典版本
数据结构·list
一叶之秋14122 天前
深入剖析List的底层实现原理
c++·list
小学生波波3 天前
最新版鸿蒙开发工具DevEco Studio保姆级安装教程
arkts·鸿蒙系统·安装教程·deveco studio·鸿蒙开发·harmonyos6
编程乐学3 天前
鸿蒙非原创--DevEcoStudio开发的奶茶点餐APP
华为·harmonyos·deveco studio·鸿蒙开发·奶茶点餐·鸿蒙大作业
小学生波波3 天前
HarmonyOS6 - 弹框选择年份和月份实战案例
鸿蒙·鸿蒙开发·弹窗组件·harmonyos6·选择年份