鸿蒙 + Python 全栈开发一个排行榜应用

背景

本项目是一个用于练习鸿蒙开发的实用小项目,前后端都覆盖到位,这有助于提升开发者的整体设计能力。

相关技能点

一、渲染控制语法

条件渲染:使用 if/else 进行条件渲染。

循环渲染:开发框架提供循环渲染(ForEach 组件)来迭代数组,并为每个数组项创建相应的组件。

二、 @State、@Prop、@Link 组件状态管理装饰器和@Builder 装饰器:

@State 装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的 build 方法进行 UI 刷新。

@Prop 与@State 有相同的语义,但初始化方式不同。@Prop 装饰的变量必须使用其父组件提供的@State 变量进行初始化,允许组件内部修改@Prop 变量,但更改不会通知给父组件,即@Prop 属于单向数据绑定。

@Link 装饰的变量可以和父组件的@State 变量建立双向数据绑定,需要注意的是:@Link 变量不能在组件内部进行初始化。

@Builder 装饰的方法用于定义组件的声明式 UI 描述,在一个自定义组件内快速生成多个布局内容。

三、组件生命周期函数

自定义组件的生命周期函数用于通知用户该自定义组件的生命周期,这些回调函数是私有的,在运行时由开发框架在特定的时间进行调用,不能从应用程序中手动调用这些回调函数,如下图所示。
在这里插入图片描述

需要注意的是,部分生命周期回调函数仅对@Entry 修饰的自定义组件生效,它们分别是:onPageShow、onPageHide、onBackPress。

四、数据请求

通过 HTTP 应用发起一个数据请求,支持常见的 GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT 方法。

五、Python Rest API

其实开发后端 API 并不困难,本项目将借助 ChatGPT 光速完成 API 的开发,大家马上就能获得到前后端打通的成就感。

代码结构

ArkTS 项目

├──entry/src/main/ets               // 代码区
│  ├──common                        // 公共文件目录
│  │  ├──bean
│  │  │  └──RankData.ets            // 实体类
│  │  └──constants
│  │     └──Constants.ets           // 常量
│  ├──entryability
│  │  └──EntryAbility.ts            // 应用的入口
│  ├──model
│  │  └──DataModel.ets              // 模拟数据
│  ├──pages
│  │  └──RankPage.ets               // 入口页面
│  ├──view                          // 自定义组件目录
│  │  ├──ListHeaderComponent.ets
│  │  ├──ListItemComponent.ets
│  │  └──TitleComponent.ets
│  └──viewmodel
│     └──RankViewModel.ets          // 视图业务逻辑类
└──entry/src/main/resources	    // 资源文件目录

Python 后端项目

使用@Link 封装标题组件

在 TitleComponent 文件中,首先使用 struct 对象创建自定义组件,然后使用@Link 修饰器管理 TitleComponent 组件内的状态变量 isRefreshData,状态变量 isRefreshData 值发生改变后,通过@Link 装饰器通知页面刷新 List 中的数据。

...
@Component
export struct TitleComponent {
  @Link isRefreshData: boolean; // 判断是否刷新数据
  @State title: Resource = $r('app.string.title_default');

  build() {
    Row() {
      ...
      Row() {
        Image($r('app.media.loading'))
          .height(TitleBarStyle.IMAGE_LOADING_SIZE)
          .width(TitleBarStyle.IMAGE_LOADING_SIZE)
          .onClick(() => {
            this.isRefreshData = !this.isRefreshData;
          })
      }
      .width(TitleBarStyle.WEIGHT)
      .height(WEIGHT)
      .justifyContent(FlexAlign.End)
    }
   ...
  }
}

封装列表头部样式组件

在 ListHeaderComponent 文件中,我们使用常规成员变量来设置自定义组件 ListHeaderComponent 的 widthValue 和 paddingValue。

@Component
export struct ListHeaderComponent {
  paddingValue: Padding | Length;
  widthValue: Length;

  build() {
    Row() {
      Text($r('app.string.page_number'))
        .fontSize(FontSize.SMALL)
        .width(ListHeaderStyle.LAYOUT_WEIGHT_LEFT)
        .fontWeight(ListHeaderStyle.FONT_WEIGHT)
        .fontColor($r('app.color.font_description'))
      Text($r('app.string.page_type'))
        .fontSize(FontSize.SMALL)
        .width(ListHeaderStyle.LAYOUT_WEIGHT_CENTER)
        .fontWeight(ListHeaderStyle.FONT_WEIGHT)
        .fontColor($r('app.color.font_description'))
      Text($r('app.string.page_vote'))
        .fontSize(FontSize.SMALL)
        .width(ListHeaderStyle.LAYOUT_WEIGHT_RIGHT)
        .fontWeight(ListHeaderStyle.FONT_WEIGHT)
        .fontColor($r('app.color.font_description'))
    }
    .width(this.widthValue)
    .padding(this.paddingValue)
  }
}

创建 ListItemComponent

为了体现@Prop 单向绑定功能,我们在 ListItemComponent 组件中添加了一个@Prop 修饰的字段 isSwitchDataSource,当通过点击改变 ListItemComponent 组件中 isSwitchDataSource 状态时,ListItemComponent 作为 List 的子组件,并不会通知其父组件 List 刷新状态。在代码中,我们使用@State 管理 ListItemComponent 中的 isChange 状态,当用户点击 ListItemComponent 时,ListItemComponent 组件中的文本颜色发生变化。我们使用条件渲染控制语句,创建的圆型文本组件。

@Component
export struct ListItemComponent {
  index: number;
  private name: Resource;
  @Prop vote: string;
  @Prop isSwitchDataSource: boolean;
  // 判断是否改变ListItemComponent字体颜色
  @State isChange: boolean = false;

  build() {
    Row() {
      Column() {
        if (this.isRenderCircleText()) {
          this.CircleText(this.index);
        } else {
          Text(this.index.toString())
            .lineHeight(ItemStyle.TEXT_LAYOUT_SIZE)
            .textAlign(TextAlign.Center)
            .width(ItemStyle.TEXT_LAYOUT_SIZE)
            .fontWeight(FontWeight.BOLD)
            .fontSize(FontSize.SMALL)
        }
      }
      .width(ItemStyle.LAYOUT_WEIGHT_LEFT)
      .alignItems(HorizontalAlign.Start)

      Text(this.name)
        .width(ItemStyle.LAYOUT_WEIGHT_CENTER)
        .fontWeight(FontWeight.BOLDER)
        .fontSize(FontSize.MIDDLE)
        .fontColor(this.isChange ? ItemStyle.COLOR_BLUE : ItemStyle.COLOR_BLACK)
      Text(this.vote)
        .width(ItemStyle.LAYOUT_WEIGHT_RIGHT)
        .fontWeight(FontWeight.BOLD)
        .fontSize(FontSize.SMALL)
        .fontColor(this.isChange ? ItemStyle.COLOR_BLUE : ItemStyle.COLOR_BLACK)
    }
    .height(ItemStyle.BAR_HEIGHT)
    .width(WEIGHT)
    .onClick(() => {
      this.isSwitchDataSource = !this.isSwitchDataSource;
      this.isChange = !this.isChange;
    })
  }
  ...
}

创建 RankList

为了简化代码,提高代码的可读性,我们使用@Builder 描述排行列表布局内容,使用循环渲染组件 ForEach 创建 ListItem。

...
  build() {
    Column() {
      // 顶部标题组件
      TitleComponent({ isRefreshData: $isSwitchDataSource, title: TITLE })
      // 列表头部样式
      ListHeaderComponent({
        paddingValue: {
          left: Style.RANK_PADDING,
          right: Style.RANK_PADDING
        },
        widthValue: Style.CONTENT_WIDTH
      })
        .margin({
          top: Style.HEADER_MARGIN_TOP,
          bottom: Style.HEADER_MARGIN_BOTTOM
        })
      // 列表区域
      this.RankList(Style.CONTENT_WIDTH)
    }
    .backgroundColor($r('app.color.background'))
    .height(WEIGHT)
    .width(WEIGHT)
  }

  @Builder RankList(widthValue: Length) {
    Column() {
      List() {
        ForEach(this.isSwitchDataSource ? this.dataSource1 : this.dataSource2,
          (item, index) => {
            ListItem() {
              ListItemComponent({ index: index + 1, name: item.name, vote: item.vote,
                isSwitchDataSource: this.isSwitchDataSource
              })
            }
          }, item => JSON.stringify(item))
      }
      .width(WEIGHT)
      .height(Style.LIST_HEIGHT)
      .divider({ strokeWidth: Style.STROKE_WIDTH })
    }
    .padding({
      left: Style.RANK_PADDING,
      right: Style.RANK_PADDING
    })
    .borderRadius(Style.BORDER_RADIUS)
    .width(widthValue)
    .alignItems(HorizontalAlign.Center)
    .backgroundColor(Color.White)
  }
...

使用自定义组件生命周期函数

通过点击系统导航返回按钮来演示 onBackPress 回调方法的使用,在指定的时间段内,如果满足退出条件,onBackPress 将返回 false,系统默认关闭当前页面。否则,提示用户需要再点击一次才能退出,同时 onBackPress 返回 true,表示用户自己处理导航返回事件。

...
@Entry
@Component
struct RankPage {
  ...
  onBackPress() {
    if (this.isShowToast()) {
      prompt.showToast({ message: $r('app.string.prompt_text'), duration: TIME });
      this.clickBackTimeRecord = new Date().getTime();
      return true;
    }
    return false;
  }
  ...
}

总结

本项目主要学习了下列知识点,为下个项目做好铺垫。

  1. 条件渲染、循环渲染语法的使用。
  2. @State、@Prop、@Link 修饰器的使用。
  3. @Builder 修饰器的使用。
  4. 自定义组件生命周期函数 onBackPress 的调用。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
(5)\\python全栈day41-50\\python全栈s3 day41;目录中文件数:10个 ├─(1) 01 python s3 day41 JS的历史以及引入方式.avi ├─(2) 02 python s3 day41 JS的基础规范.avi ├─(3) 03 python s3 day41 JS的基本数据类型.avi ├─(4) 04 python s3 day41 JS的运算符.avi ├─(5) 05 python s3 day41 JS的控制语句与循环.avi ├─(6) 06 python s3 day41 JS的循环与异常.avi ├─(7) 07 python s3 day41 JS的字符串对象.avi ├─(8) 08 python s3 day41 JS的数组对象.avi ├─(9) 09 python s3 day41 JS的函数对象.avi ├─(10) day41.rar (6)\\python全栈day41-50\\python全栈s3 day42;目录中文件数:6个 ├─(11) 01 python s3 day42 JS的函数作用域.avi ├─(12) 02 python s3 day42 JS的window对象之定时器.avi ├─(13) 03 python s3 day42 JS的history对象和location对象.avi ├─(14) 04 python s3 day42 JS的DOM节点.avi ├─(15) 05 python s3 day42 JS的DOM节点.avi ├─(16) day42.rar (7)\\python全栈day41-50\\python全栈s3 day43;目录中文件数:10个 ├─(17) 01 python s3 day43 上节知识回顾.avi ├─(18) 02 python s3 day43 js之onsubmit事件与组织事件外延.avi ├─(19) 03 python s3 day43 DOM节点的增删改查与属性设值.avi ├─(20) 04 python s3 day43 正反选练习.avi ├─(21) 05 python s3 day43 js练习之二级联动.avi ├─(22) 06 python s3 day43 jquery以及jquery对象介绍.avi ├─(23) 07 python s3 day43 jquery选择器.avi ├─(24) 08 python s3 day43 jquery的查找筛选器.avi ├─(25) 09 python s3 day43 jquery练习之左侧菜单.avi ├─(26) day43课件代码.rar (8)\\python全栈day41-50\\python全栈s3 day44;目录中文件数:10个 ├─(27) 01 python s3 day44 jquery属性操作之html,text,val方法.avi ├─(28) 02 python s3 day44 jquery循环方法和attr,prop方法.avi ├─(29) 03 python s3 day44 jquery模态对话框与clone的应用.avi ├─(30) 04 python s3 day44 jqueryCSS操作之offsets,position以及scrolltop.avi ├─(31) 05 python s3 day44 jquery事件绑定与事件委托.avi ├─(32) 06 python s3 day44 jquery动画效果.avi ├─(33) 07 python s3 day44 jquery扩展与插件.avi ├─(34) 08 python s3 day44 jquery扩展补充.avi ├─(35) 09 python s3 day44 本周作业轮播图以及思路.avi ├─(36) day44课件代码.rar (9)\\python全栈day41-50\\python全栈s3 day45;目录中文件数:3个 ├─(37) day45.rar ├─(38) 轮播图片css部分.avi ├─(39) 轮播图片js部分.avi (10)\\python全栈day41-50\\python全栈s3 day46;目录中文件数:8个 ├─(40) 01 python s3 day46 数据库与dbms的概念.avi ├─(41) 02 python s3 day46 sql规范.avi ├─(42) 03 python s3 day46 数据库操作DDL.avi ├─(43) 04 python s3 day46 mysql的数据类型.a

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

热爱技术的小胡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值