HarmonyOS 项目开发练习 「钢笔单词」 #3 首页、书架页、用户选项页 涉及弹性布局(FLEX)、用户首选项、关系型数据库

HarmonyOS | 项目开发练习 「钢笔单词」 #3 首页、书架页、用户选项页 | 涉及弹性布局(FLEX)、用户首选项、关系型数据库

项目结构:

在这里插入图片描述

首页:

​ 首页的重点是统计单词量的卡片,但是只实现了UI,实际上不能进行单词量统计。卡片上有一个日期选择器,卡片UI本身的颜色会根据单词量情况而改变,小于等于300为蓝色,否则为红色。卡片可以翻页,第二页设计用于记录背诵的单词总数和单日记录。

​ 首页使用 Tabs 与 TabContent 实现不同子页面卡片的切换。

Index.ets 构成整个应用程序核心页面的骨架

``

import BookIndex from '../view/book_page/BookIndex'
import HomeIndex from '../view/home_page/HomeIndex'
import UserIndex from '../view/user_page/UserIndex'
@Entry
@Component
struct Index {
  @State currentIndex: number = 0
  @Builder TabBarBuilder(title: ResourceStr, image: ResourceStr, index: number){
    Column(){
      Image(image).width(33)
        .fillColor(this.currentIndex === index ? Color.White : $r('app.color.gray'))
    }
    .backgroundColor(this.currentIndex === index ? $r('app.color.lightest_primary_color') : Color.White)
    .width('80%').height('100%').justifyContent(FlexAlign.Center).margin({left: 20})
  }

  build() {
    Stack(){
      // 直接给父组件设置背景图片,在设备反转时,背景图片不转动,留下空白,不知道为什么没有自动旋转
      Image($r('app.media.1719303607445')).width('100%').height('100%')
      Tabs(){
        TabContent(){
          HomeIndex()
        }.tabBar(this.TabBarBuilder($r('app.string.tab_home_page'), $r('app.media.home'), 0))
        TabContent(){
          BookIndex();
        }.tabBar(this.TabBarBuilder($r('app.string.tab_music_hall'), $r('app.media.bookshelf'), 1))
        TabContent(){
          UserIndex()
        }.tabBar(this.TabBarBuilder($r('app.string.tab_user'), $r('app.media.user10'), 2))
      }.vertical(true).onChange(index => this.currentIndex = index)
    }
  }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接下来是被 Index.ets 调用的各个页面卡片,这些页面卡片里面又各自包含了一些模块。

HomeIndex.ets 构成首页

在这里插入图片描述

``

import Header from '../../common/Components/Header'
import StatisticsCard from './StatisticsCard'
@Component
export default struct HomeIndex {
  build() {
    Flex({direction: FlexDirection.Column}){
      Header({text: '首页'})
      Divider().margin({top: 10, bottom: 30})

      Flex({justifyContent: FlexAlign.SpaceEvenly}){
        StatisticsCard({color: '#ffffffff' })
        StatisticsCard({color: '#ffffffff'})
      }
    }.width('96%').height('90%')
    .backgroundColor(Color.White)
    // .opacity(0.7).backdropBlur(30) // 毛玻璃
    .shadow({radius: 5, color: $r('app.color.light_primary_color')}).borderRadius(10)
  }
}

Header.ets 通用顶部栏

在这里插入图片描述

``

@Component
export default struct SearchHeader {
  text: string
  build() {
    Flex({direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceBetween}){
      Text(this.text).fontSize(40).margin({left: 35}).fontWeight(FontWeight.Bold)
      // @ts-ignore
      Search({placeholder: $r('app.string.search_text')})
        .textFont({size: 20}).backgroundColor('#ffffffff').placeholderColor('#ff2f2f2f')
        .searchButton('搜索').width('50%').borderWidth(1)
      Badge({value: '!', position: BadgePosition.Left, style: {fontSize: 16}}){
        Image($r('app.media.mail')).fillColor($r('app.color.light_primary_color')).width(40).margin({left: 10, right: 10})
      }
    }
  }
}

StatisticsCard.ets 构成统计卡片的骨架,这个卡片可以进行翻页

在这里插入图片描述

``

import DateUtil from '../../common/utils/DateUtil'
import DatePickDialog from './DatePickDialog'
import SummaryStat from './SummaryStat'
import TodayStatistics from './TodayStatistics'
@Component
export default struct StatisticsCard {
  color: ResourceStr
  @StorageProp('selectedDate') selectedDate: number = DateUtil.beginTimeOfDay(new Date()) // 获取开始日期
  controller: CustomDialogController = new CustomDialogController({
    builder: DatePickDialog({selectedDate: new Date(this.selectedDate)})
  })
  build() {
    Flex({direction: FlexDirection.Column}){
      Row(){
        Image($r('app.media.more')).width(30).fillColor(Color.Black)
        Text(DateUtil.formatDate(this.selectedDate)).fontColor(Color.Black).fontSize(30)
      }.width('100%').backgroundColor(Color.White).padding(10)
      .onClick(() => this.controller.open())
      Swiper(){
        TodayStatistics()
        SummaryStat()
      }.width('100%').height('80%').borderRadius(10).indicatorStyle({selectedColor: Color.White}).loop(true)
    }.backgroundColor(this.color).width('45%').height('35%').borderRadius(10)
    .shadow({radius: 5, color: $r('app.color.light_primary_color')})
  }
}

TodayStatistics.ets 卡片的第一个页面

在这里插入图片描述

``

@Component
export default struct TodayStatistics {
  @State count: number = 80 // 今日背词总数
  readonly healthNum: number = 300 // 安全次数上限
  build() {
    Flex({justifyContent: FlexAlign.SpaceAround, alignItems: ItemAlign.Center}){
      Flex({direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
        Text('今日词汇量').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)
        Text((this.count).toString()).fontSize(30).fontColor(Color.White)
        Text(this.count <= this.healthNum ? '适宜' : '过量').fontColor(Color.White)
      }
      Progress({
        value: this.count,
        total: this.healthNum,
        type: ProgressType.Eclipse
      }).width('20%').color(Color.White)
      Text('记录').fontSize(40).fontColor(Color.White).fontWeight(FontWeight.Bold)
    }.padding(30).backgroundColor(this.count <= this.healthNum ? $r('app.color.primary_color') : '#ffff2e4f')
  }
}

SummaryStat.ets 卡片的第二个页面

在这里插入图片描述

``

@Component
export default struct SummaryStat {
  @State historyCount: number = 23540 // 听歌总数
  @State dayCount: number = 30 // 单日最高记录
  build() {
    Flex({ justifyContent: FlexAlign.SpaceAround, alignItems: ItemAlign.Center }) {
      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
        Text('单日最高记录').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)
        Text((this.dayCount).toString()).fontSize(30).fontColor(Color.White)
      }
      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
        Text('历史次数').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)
        Text((this.historyCount).toString()).fontSize(30).fontColor(Color.White)
      }
    }.padding(30).backgroundColor('#ff2d4167')
  }
}

DatePickDialog.ets 日期弹窗

在这里插入图片描述

``

import CommonConstants from '../../common/constants/CommonConstants'
@CustomDialog
export default struct DatePickDialog {
  controller: CustomDialogController
  selectedDate: Date = new Date()
  build() {
    Column({space: CommonConstants.SPACE_10}){
      DatePicker({ // 日期选择器
        start: new Date('2012-01-01'),
        end: new Date(),
        selected: this.selectedDate
      })
        .onChange((value: DatePickerResult) => {
          this.selectedDate.setFullYear(value.year, value.month, value.day)
          console.info('select current date is: ' + JSON.stringify(value))
        })
      Row({space: CommonConstants.SPACE_10}){
        Button($r('app.string.cancel_label')).backgroundColor($r('app.color.lightest_primary_color')).width('40%')
          .onClick(() => {this.controller.close()})
        Button($r('app.string.confirm_label')).backgroundColor($r('app.color.primary_color')).width('40%')
          .onClick(() => {
            AppStorage.SetOrCreate('selectedDate', this.selectedDate.getTime()) // 将日期保存到全局存储
            this.controller.close()
          })
      }
    }.padding(CommonConstants.SPACE_10)
  }
}

书架页面:

​ 这个页面是进入背单词页面的入口,仅实现了路由跳转功能。

在这里插入图片描述

BookIndex.ets 构成书架页面

``

import Header from '../../common/Components/Header'
import Book from '../../viewModel/Book'
import BookCard from './BookCard'
import router from '@ohos.router'
@Component
export default struct BookIndex {
  cardMargin_left: number = 20 // 词书卡片的margin

  build() {
    Flex({direction: FlexDirection.Column}){
      Header({text: '书架'})
      Divider().margin({top: 10, bottom: 30})

      List({space: 25}){
        ListItem(){
          Flex(){
            BookCard({book: new Book('生词本', null, 'CET-4', '无', '用户'), color: '#ff67398c', lightColor: '#ff8864be'}).margin({left: this.cardMargin_left})
              .onClick(() => {
                router.pushUrl({
                  url: 'pages/LearningIndex',
                  params: {}
                },
                  router.RouterMode.Single,
                  e => {
                    if(e){
                      console.log('路由失败>>>' + e.code + '>>>' + e.message)
                    }
                  }
                )
              })
          }
        }.margin({top: 5})
        ListItem(){
          Text('四级专区').fontColor('#ff606060')
        }.width('100%').backgroundColor('#ffefefef')
        ListItem(){
          Flex(){
            BookCard({book: new Book('四级大纲词汇', null, 'CET-4', '无', '官方'), color: '#ff943341', lightColor: '#ffb85866'}).margin({left: this.cardMargin_left})
            BookCard({book: new Book('四级核心词汇', null, 'CET-4', '无', '官方'), color: '#ff2e8120', lightColor: '#ff589f4c'}).margin({left: this.cardMargin_left})
          }
        }
        ListItem(){
          Text('六级专区').fontColor('#ff606060')
        }.width('100%').backgroundColor('#ffefefef')
        ListItem(){
          Flex(){
            BookCard({book: new Book('六级大纲词汇', null, 'CET-6', '无', '官方'), color: '#ff293f91', lightColor: '#ff4f63ac'}).margin({left: this.cardMargin_left})
            BookCard({book: new Book('六级核心词汇', null, 'CET-6', '无', '官方'), color: '#ff196177', lightColor: '#ff448599'}).margin({left: this.cardMargin_left})
            BookCard({book: new Book('六级真题词汇', null, 'CET-6', '无', '官方'), color: '#ff286e3c', lightColor: '#ff529164'}).margin({left: this.cardMargin_left})
          }
        }
        ListItem(){
          Text('导入').fontColor('#ff606060')
        }.width('100%').backgroundColor('#ffefefef')
        ListItem(){
          Flex(){
            BookCard({book: new Book('骑士专用词汇', null, 'OTHER', '无', '用户'), color: '#ff323232', lightColor: '#ff5a5a5a'}).margin({left: this.cardMargin_left})
          }
        }
        ListItem(){
          Text('.')
        }
      }.width('100%')
      .layoutWeight(1)
      .alignListItem(ListItemAlign.Center)

    }.width('96%').height('90%')
    .backgroundColor(Color.White)
    // .opacity(0.7).backdropBlur(30) // 毛玻璃
    .shadow({radius: 5, color: $r('app.color.light_primary_color')}).borderRadius(10)
  }
}

BookCard.ets 书架里的词书卡片

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

``

import Book from '../../viewModel/Book'
@Component
export default struct BookCard {
  private book: Book
  private color: string
  private lightColor: string
  build() {
    Flex({direction: FlexDirection.Column}){
      Text(this.book.type).fontSize(25)
      Flex({direction: FlexDirection.Column}){
        Text(this.book.bookName).fontSize(40).fontColor(Color.White).margin({top: 10})
        Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
          Text()
        }.backgroundColor(this.lightColor).height('68%')
      }.backgroundColor(this.color).width('100%').borderRadius(10)
    }.backgroundImage(this.book.bookBackground).width('30%').height('60%').borderRadius(10)
    .shadow({radius: 5, color: $r('app.color.light_primary_color')})
  }
}

选项页面

​ 并没有实现任何功能的页面。

在这里插入图片描述

UserIndex.ets 构成选项页面

``

import Header from '../../common/Components/Header'
import CommonUserCard from './CommonUserCard'
import WordBoxCard from './WordBoxCard'
@Component
export default struct UserIndex {
  build() {
    Flex({direction: FlexDirection.Column}){
      Header({text: '选项'})
      Divider().margin({top: 10, bottom: 30})

      Flex({justifyContent: FlexAlign.SpaceEvenly, }){
        CommonUserCard({text: '迪莫骑士', icon: $r('app.media.userIcon')})
        WordBoxCard({text: '词书工具箱', icon: $r('app.media.wordb')})
      }
    }.width('96%').height('90%').backgroundColor(Color.White)
    // .opacity(0.7).backdropBlur(30) // 毛玻璃
    .shadow({radius: 5, color: $r('app.color.light_primary_color')}).borderRadius(10)
  }
}

CommonUserCard.ets 左卡片

在这里插入图片描述

``

@Component
export default struct CommonUserCard {
  private text: ResourceStr
  private icon: ResourceStr
  build() {
    Flex({direction: FlexDirection.Column}){
      Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
        Image(this.icon).fillColor($r('app.color.primary_color')).height('95%')
          .interpolation(ImageInterpolation.High)
        Text(this.text).fontSize(30)
      }.height('20%')
      Flex({direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceEvenly}){
        Row(){
          // 资料卡按钮
          Column(){
            Image($r('app.media.acst')).fillColor(Color.White).width('80%')
            Blank()
            Text('信息设置').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ffb36068').borderRadius(10)

          // 资料卡按钮
          Column(){
            Image($r('app.media.acst')).fillColor(Color.White).width('80%')
            Blank()
            Text('目标设置').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff6667ab').borderRadius(10)
        }.width('100%').justifyContent(FlexAlign.SpaceEvenly)

        Row(){
          // 资料卡按钮
          Column(){
            Image($r('app.media.acst')).fillColor(Color.White).width('80%')
            Blank()
            Text('记录查看').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff3f7070').borderRadius(10)

          // 资料卡按钮
          Column(){
            Image($r('app.media.acst')).fillColor(Color.White).width('80%')
            Blank()
            Text('清空记录').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff6a4b80').borderRadius(10)
        }.width('100%').justifyContent(FlexAlign.SpaceEvenly)
      }.backgroundColor($r('app.color.primary_color')).width('100%').borderRadius(10)
    }.width('40%').height('80%')
    .shadow({radius: 5, color: $r('app.color.light_primary_color')}).borderRadius(10).margin({top: 15})
  }
}

WordBoxCard.ets 右卡片

在这里插入图片描述

``

@Component
export default struct WordBoxCard {
  private text: ResourceStr
  private icon: ResourceStr
  build() {
    Flex({direction: FlexDirection.Column}){
      Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}){
        Image(this.icon).fillColor($r('app.color.primary_color')).height('95%')
          .interpolation(ImageInterpolation.High)
        Text(this.text).fontSize(30)
      }.height('20%')
      Flex({direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceEvenly}){
        Row(){
          // 资料卡按钮
          Column(){
            Image($r('app.media.addword')).fillColor(Color.White).width('78%')
            Blank()
            Text('增加生词').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ffb36068').borderRadius(10)
          .onClick(() => {

          })

          // 资料卡按钮
          Column(){
            Image($r('app.media.addword')).fillColor(Color.White).width('78%')
            Blank()
            Text('删除生词').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff6667ab').borderRadius(10)
        }.width('100%').justifyContent(FlexAlign.SpaceEvenly)

        Row(){
          // 资料卡按钮
          Column(){
            Image($r('app.media.addword')).fillColor(Color.White).width('78%')
            Blank()
            Text('导入词书').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff3f7070').borderRadius(10)

          // 资料卡按钮
          Column(){
            Image($r('app.media.addword')).fillColor(Color.White).width('78%')
            Blank()
            Text('删除词书').fontColor(Color.White).fontSize(30)
          }.width('45%').height('45%').backgroundColor('#ff6a4b80').borderRadius(10)
        }.width('100%').justifyContent(FlexAlign.SpaceEvenly)
      }.backgroundColor($r('app.color.primary_color')).width('100%').borderRadius(10)
    }.width('40%').height('80%')
    .shadow({radius: 5, color: $r('app.color.light_primary_color')}).borderRadius(10).margin({top: 15})
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayHsu_蔚蓝审敛法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值