鸿蒙应用开发笔记[2]-简单界面布局

发布时间 2023-12-03 17:31:15作者: qsBye

摘要

在HarmonyOS4开发一个应用,在ArkTS的Stage开发范式下简单进行界面布局.

平台信息

  • DevEco Studio 4.0 Release 构建版本:4.0.0.600, built on October 17, 2023
  • HarmonyOS 4
  • Compile SDK “3.1.0(API 9)”

原理简介

ArtTS的Stage开发框架

[https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/start-with-ets-stage-0000001477980905-V3]

  • AppScope > app.json5:应用的全局配置信息。
  • entry:HarmonyOS工程模块,编译构建生成一个HAP包。
  • src > main > ets:用于存放ArkTS源码。
  • src > main > ets > entryability:应用/服务的入口。
  • src > main > ets > pages:应用/服务包含的页面。
  • src > main > resources:用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件,详见资源分类与访问。
  • src > main > module.json5:Stage模型模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明,详见module.json5配置文件。
  • build-profile.json5:当前的模块信息、编译信息配置项,包括buildOption、targets配置等。其中targets中可配置当前运行环境,默认为HarmonyOS。
  • hvigorfile.ts:模块级编译构建任务脚本,开发者可以自定义相关任务和代码实现。
  • oh_modules:用于存放三方库依赖信息。关于原npm工程适配ohpm操作,请参考历史工程迁移。
  • build-profile.json5:应用级配置信息,包括签名、产品配置等。
  • hvigorfile.ts:应用级编译构建任务脚本。

界面布局方式

[https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/arkts-layout-development-overview-0000001450866508-V3]
声明式UI提供了以下8种常见布局,开发者可根据实际应用场景选择合适的布局进行页面开发。

布局类型 描述 使用场景
线性布局(Row、Column) 子元素超过1个且能线性排列时优先考虑此布局 需要线性排列的布局场景
层叠布局(Stack) 组件需要堆叠效果时优先考虑此布局,不影响其他子组件布局空间 需要组件堆叠效果的场景,如弹出Panel覆盖其他组件
弹性布局(Flex) 子组件需要计算拉伸或压缩比例时优先使用此布局,实现更好的视觉填充效果 需要子组件拉伸或压缩的场景
相对布局(RelativeContainer) 二维空间中的自由布局方式,通过设置锚点规则对齐子组件位置 页面元素分布复杂或需要避免深层嵌套的场景
栅格布局(GridRow、GridCol) 将空间分割为有规律的栅格,实现不同设备下的不同布局,降低适配成本 手机、大屏、平板等不同设备上的多场景布局需求
媒体查询(@ohos.mediaquery) 根据不同设备类型或状态修改应用样式,屏幕动态改变时更新页面布局 需要根据设备和应用属性信息设计不同布局的场景
列表(List) 显示结构化、可滚动的信息,具有垂直和水平布局能力和自适应排列个数的布局能力 用于呈现同类数据类型或数据类型集的场景,如图片和文本列表
网格(Grid) 具有页面均分能力、子组件占比控制能力,是一种重要的自适应布局方式 需要按照固定比例或均匀分配空间的布局场景,如计算器、相册、日历等
轮播(Swiper) 实现广告轮播、图片预览、可滚动应用等功能的组件 广告轮播、图片预览等需要滚动的场景

实现

主要代码

Index.ets
默认页面固定为Index.ets,不能修改名称.

@Entry
@Component
struct Index {
  // 智能体问候消息
  @State agent_greet_message: string = "晚上好!天气转凉,出门记得添衣哦?";
  // 天气
  @State weather_message: string = "乌鲁木齐 -1℃ 雨夹雪";
  // 宿舍环境信息
  @State room1_env_message: string = "宿舍 20.1℃ 湿度:40% 亮度:1000 Lux";
  // 走廊环境信息
  @State room2_env_message: string = "走廊 18.0℃ 湿度:42% 亮度:680 Lux";
  // 课室环境信息
  @State room3_env_message: string = "课室 18.2℃ 湿度:39% 亮度:100 Lux";
  // 实验室环境信息
  @State room4_env_message: string = "实验室 18.0℃ 湿度:20% 亮度:101 Lux";

  // 声明式页面布局
  build() {
    Flex({ wrap: FlexWrap.Wrap }) {
      // start 智能助手容器,内部竖向排列
      Column({ space: 4 }) {
        // 智能体问候
        Text(this.agent_greet_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)
          .padding(25)

        // TODO: 语音识别图标及声音波形
      }
      .width('46%')
      .border({
        radius: {
          topLeft: $r('app.float.default_24'),
          topRight: $r('app.float.default_24'),
          bottomLeft:$r('app.float.default_24'),
          bottomRight:$r('app.float.default_24')
        }
      })
      .backgroundColor("#fff")
      .margin({left:10}) // 外边距
      .padding({top:10,bottom:10})// 内边距
      // end 智能助手容器

      // start 环境信息容器,内部竖向排列
      Column({ space: 4 }) {
        // 标题:环境
        Text("环境")
          .fontSize(16)
          .fontWeight(FontWeight.Bolder)

        // 室外天气
        Text(this.weather_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)

        // 宿舍环境信息
        Text(this.room1_env_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)

        // 走廊环境信息
        Text(this.room2_env_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)

        // 课室环境信息
        Text(this.room3_env_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)

        // 实验室环境信息
        Text(this.room4_env_message)
          .fontSize(8)
          .fontWeight(FontWeight.Bold)

      }
      .width('46%')
      .border({
        radius: {
          topLeft: $r('app.float.default_24'),
          topRight: $r('app.float.default_24'),
          bottomLeft:$r('app.float.default_24'),
          bottomRight:$r('app.float.default_24')
        }
      })
      .backgroundColor("#fff")
      .margin({left:10}) // 外边距
      .padding({top:10,bottom:10}) // 内边距
      // end 环境信息容器

      // start 灯光控制容器
      Column({ space: 4 }) {
        // 标题
        Text("灯光")
          .fontSize(16)
          .fontWeight(FontWeight.Bolder)

        // 落地灯按钮
        Row() {
          Text("落地灯")
            .height(50)
            .padding({left: 10})
            .fontSize(8)
            .textAlign(TextAlign.Start)
            .backgroundColor(0xFFFFFF)
            .fontWeight(FontWeight.Bold)
          Toggle({ type: ToggleType.Switch })
            .padding({right: 10}) // 外边距
            .onChange((isOn: boolean) => {
                if(isOn) {
                  promptAction.showToast({ message: '落地灯已打开' })
                } else {
                  promptAction.showToast({ message: '落地灯已关闭' })
                }
                })// end onChange
          }
          .backgroundColor(0xFFFFFF)

      }
      .width('46%')
      .border({
        radius: {
          topLeft: $r('app.float.default_24'),
          topRight: $r('app.float.default_24'),
          bottomLeft:$r('app.float.default_24'),
          bottomRight:$r('app.float.default_24')
        }
      })
      .backgroundColor("#fff")
      .margin({left:10}) // 外边距
      .padding({top:10,bottom:10}) // 内边距
      // end 灯光控制容器

      // start 风扇控制容器
      Column({ space: 4 }) {
        // 标题
        Text("风扇")
          .fontSize(16)
          .fontWeight(FontWeight.Bolder)

        // 落地灯按钮
        Row() {
          Text("空气净化器")
            .height(50)
            .padding({left: 10})
            .fontSize(8)
            .textAlign(TextAlign.Start)
            .backgroundColor(0xFFFFFF)
            .fontWeight(FontWeight.Bold)
          Toggle({ type: ToggleType.Switch })
            .padding({right: 10}) // 外边距
            .onChange((isOn: boolean) => {
              if(isOn) {
                promptAction.showToast({ message: '空气净化器已打开' })
              } else {
                promptAction.showToast({ message: '空气净化器已关闭' })
              }
            })// end onChange
        }
        .backgroundColor(0xFFFFFF)

      }
      .width('46%')
      .border({
        radius: {
          topLeft: $r('app.float.default_24'),
          topRight: $r('app.float.default_24'),
          bottomLeft:$r('app.float.default_24'),
          bottomRight:$r('app.float.default_24')
        }
      })
      .backgroundColor("#fff")
      .margin({left:10,top:10}) // 外边距
      .padding({top:10,bottom:10}) // 内边距
      // end风扇控制容器

    }// end Flex

    .width('100%')
    .height('100%')
    .padding({ top: 10, bottom: 10 })// 内边距
    .backgroundColor("#eef")
  }// end build

更换应用名称和图标:
modules.json5

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ts",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:weekend_panel_logo",
        "label": "$string:app_name",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ]
  }
}

更改iconlabel,具体值可以直接定义或者引用resources文件夹中的内容(参考Android开发).

效果