文档管理中心
最佳实践一次开发,多端部署多设备界面开发多设备界面开发案例多设备股票类界面

多设备股票类界面

概述

本文以当前流行的垂类市场中的股票类应用为典型案例,详细介绍“一多”在实际开发中的应用,主要涵盖自选股和个股详情两个典型页面,展示其在直板机、双折叠、三折叠、阔折叠、平板和电脑六种产品形态上的“一次开发,多端部署”。下文将从UX设计、工程管理、移动端页面、电脑端页面四个角度,介绍“一多”股票类应用在开发过程中的最佳实践。

说明

阅读本文前,建议开发者先了解ArkUI(方舟UI框架)一次开发,多端部署概览相关知识。

下文将从UX设计、工程管理、页面开发三个角度,介绍股票类应用在多设备开发中的最佳实践。

  • UX设计:介绍股票类应用的交互逻辑和通用设计要点,对于类似的设计要点,开发者可以直接参考。
  • 工程管理:推荐“一多”项目采用分层架构,明确各层逻辑。同时,介绍股票类应用适用的三层架构配置。
  • 移动端页面电脑端页面:遵循实际应用开发流程,以页面为基本单元,分别讲解移动端、电脑端页面在窗口适配、页面开发和功能开发的设计思路与实现方法。

UX设计

股票类应用的目的是让用户更便捷地办理金融业务。常见类型包括理财、股票、基金等应用及业务场景,核心场景有数据查看和股票交易等。

股票类应用有以下特点:

  • 丰富的信息聚合。
  • 图表数据高效展示。
  • 便捷高效的交互方式。

此类型的应用在多端设备的使用过程中,要保障用户办理金融业务时的基本体验与功能可用性,也需着力优化大屏幕设备上的交互效率。

股票类应用的UX设计可参考金融理财类多设备响应式设计指南的自选股章节,设计参考图如下所示。

工程管理

创建工程

建议开发者参考多设备工程部署与发布相关内容,掌握分层架构工程的创建与配置方法后,创建出模板项目工程。然后根据股票类应用的开发需求进行针对性修改,确保工程架构贴合实际业务需求。

工程结构

在创建“一多”工程时,开发者会面临工程结构目录的划分问题。考虑到复用性和可维护性,本文以股票类应用为例,提供推荐的参考方案。

HarmonyOS的分层架构包括产品定制层、基础特性层和公共能力层,为开发者提供清晰、高效、可扩展的设计架构。详细请参见分层架构设计

股票类应用根据一多推荐的commons、features、products的"三层工程架构"划分目录。其中,features(基础特性层)按业务功能划分为三个独立模块:股票交易-stockdeal(包含买入弹窗、K线图表、交易详情等组件)、股票详情-stockdetail(包含股票详情页、多窗口入口、信息表格等)以及股票市场-stockmarket(包含市场列表组件)。公共常量、媒体播放工具以及窗口管理工具等需要被不同页面依赖引用的内容,划分为一个commons(公共能力层):基础能力-base。products(产品定制层)包含default和pc两个模块定制了程序标准启动流程和多场景协同场景的入口能力。

工程结构如下(目录层级):
收起
自动换行
深色代码主题
复制
  1. ├──commons
  2. └──base/src/main
  3. └──ets
  4. ├──baseviews // 公共视图组件
  5. ├──models // 公共数据模型
  6. └──utils // 公共工具类
  7. ├──features
  8. ├──stockdeal/src/main
  9. └──ets
  10. ├──chartmodels // 图表组件
  11. ├──models // 股票交易数据模型
  12. ├──viewmodels // 股票交易视图模型
  13. └──views // 股票交易视图组件
  14. ├──stockdetail/src/main
  15. ├──ets
  16. ├──models // 股票详情数据模型
  17. ├──viewmodels // 股票详情视图模型
  18. ├──pages // 股票详情页
  19. └──views // 股票详情视图组件
  20. └──stockmarket/src/main
  21. └──ets
  22. ├──models // 股票市场数据模型
  23. ├──viewmodels // 股票市场视图模型
  24. └──views // 股票市场视图组件
  25. └──products
  26. ├──default/src/main
  27. ├──ets
  28. ├──entryability // 移动端程序入口
  29. ├──entrybackupability // 程序备份入口
  30. ├──pages // 移动端首页
  31. ├──splitScreenAbility // 分屏能力
  32. └──splitScreenBackupAbility // 分屏备份能力
  33. └──resources // 应用静态资源目录
  34. └──pc/src/main
  35. ├──ets
  36. ├──pages // PC端页面
  37. ├──pcability // PC端程序入口
  38. └──pcbackupability // PC程序备份入口
  39. └──resources // 应用静态资源目录

移动端页面

窗口适配

窗口模式

多设备股票类界面示例,根据适配的设备,涉及全屏模式、分屏模式、悬浮窗模式、自由窗口模式,可参考窗口模式。其中分屏模式与悬浮窗通常无特殊设计,可通过系统方式进入。应用监听窗口尺寸变化,通过断点刷新UI,将自动适配全屏、分屏、悬浮窗、自由窗口模式下的布局。

使用系统UI组件进入全景多窗,实现一个应用多个窗口并行运行的体验,可参考股票详情页——功能开发:应用多实例-多股比价部分。

窗口方向

通过设置setPreferredOrientation()使应用跟随传感器自动旋转。在类直板机上推荐仅竖屏显示,在双折叠展开态、三折叠G态、平板等大屏幕场景下推荐四方向旋转并受控制中心的旋转开关控制。在股票应用中,通过module.json5配置文件,建议设置为FOLLOW_DESKTOP,具体说明可参考窗口方向

窗口沉浸式

根据UX设计,实现不同窗口模式(全屏、分屏、悬浮窗)下窗口的沉浸式,可参考窗口沉浸式。全屏、分屏和悬浮窗的沉浸式均可通过setWindowLayoutFullscreen()实现,并进行动态安全区避让。

自选股页面

自选股页主要用于响应用户输入、展示指数、自选股票信息以及跳转股票详情页。按照功能设计,将自选股页相关内容划分为4个区域,效果图如下所示:
展开

横向(/纵向)断点

sm/md

sm/lg

md

lg

自选股页

界面开发

对各个区域使用的多种能力进行分析,实现方案如下表:
展开

区域编号

简介

实现方案

1

底部/侧边页签

借助响应式组件HdsTabs实现。同时在api版本低的设备上降级使用Tabs组件。

2

指数

最后一个组件固定,其他组件使用List实现延伸能力,随着设备宽度变大,页签间距变大,页面能够展示更多页签内容。

3

股票列表-工具栏

文字和功能按钮中间增加Blank,实现拉伸能力。

4

股票列表

通过使用List设置固定宽度和Scroll,可实现股票列表数据的上下或左右滑动。同时,支持对不同列设置不同的justifyContent,以便实现各列的不同对齐方式。

  • 整个页面使用的是分栏布局,在股票列表区域,点击某一股票时,平板上会分栏显示该股票的详细信息。

股票详情页

股票详情页主要用于响应用户输入、展示具体股票详细信息以及查看讨论信息等内容。按照功能设计,将自选股页相关内容划分为6个区域,效果图如下所示:
展开

横向(/纵向)断点

sm/md

sm/lg

md

lg、xl

个股详情页

界面开发

对各区域使用的能力进行分析,实现方案如表所示:
展开

区域编号

简介

实现方案

1

交易操作行

通过为“去交易”按钮设置layoutWeight布局权重,并使用Blank组件结合断点,实现该按钮的自适应拉伸。

2

标题

居中显示,其他操作两端对齐,空白空间使用Blank实现自适应布局拉伸能力。

3

行情列表数据

通过栅格并结合断点,控制在不同断点下显示不同的列数,列表自适应两列变多列。

4

中间Tab

通过List的space属性并结合断点,控制在不同断点下ListItem之间的间距。

5

曲线图和柱状图

使用layoutWeight属性实现拉伸能力。

6

讨论Tab

通过List的space属性并结合断点,控制在不同断点下ListItem之间的间距。

功能开发:应用多实例-多股比价

应用通过系统提供的MultiWindowEntryInAPP组件,配置需拉起的bundleName与UIAbility(仅限本应用,无法拉起其他应用),单击组件页面进入分屏(双股对比),在分屏状态下,再点击组件进入全景多窗(三股对比)。

下表以Mate X5设备为例,展示应用在分屏及全景多窗模式下的效果。

展开

-

折叠屏分屏-双股比价

折叠屏全景多窗-三股比价

个股详情页-多股比价

约束条件

MultiWindowEntryInAPP组件依赖全景多窗特性,只有当前设备及屏幕状态支持全景多窗,才支持设置此功能。目前支持全景多窗的设备型态有:

  • 双折叠:展开态。
  • 三折叠:双屏态,三屏态的横屏态。
  • 平板:横屏态。

对于不支持的设备型态,该组件不可交互,不响应点击事件。

说明

建议开发者在分屏副窗口左上角设置关闭按钮以直接关闭副窗口,本案例使用返回按钮,是股票比价场景需返回上级页面的特定需求。

开发步骤

应用使用MultiWindowEntryInAPP组件主动分屏或进入全景多窗。具体开发步骤如下:

  1. 导入模块。
    收起
    自动换行
    深色代码主题
    复制
    1. import { MultiWindowEntryInAPP } from '@kit.UIDesignKit';
    2. import { TextModifier } from '@kit.ArkUI';
    3. import { Want } from '@kit.AbilityKit';
  2. 使用MultiWindowEntryInAPP组件,并且设置组件参数。
    收起
    自动换行
    深色代码主题
    复制
    1. @Component
    2. export struct MultiWindowEntryComponent {
    3. @Link textModifier: TextModifier;
    4. @Link want: Want;
    5. @State isShowMultiWindowEntry: boolean = false;
    6. // ...
    7. build() {
    8. Row() {
    9. MultiWindowEntryInAPP({
    10. want: this.want,
    11. isShowSubtitle: false,
    12. multiWindowEntryInAPPStyle: {
    13. iconOptions: {
    14. iconSize: 24,
    15. iconColor: $r('sys.color.font_primary'),
    16. iconWeight: FontWeight.Normal,
    17. backgroundColor: $r('sys.color.comp_background_tertiary')
    18. },
    19. subtitleOptions: {
    20. modifier: this.textModifier.fontColor($r('app.color.text_primary_color'))
    21. }
    22. }
    23. })
    24. .id('MultiWindowEntryInAPP')
    25. }
    26. .visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)
    27. }
    28. }
  3. 导入封装好的MultiWindowEntryComponent组件,并且设置组件参数。
    收起
    自动换行
    深色代码主题
    复制
    1. import { MultiWindowEntryComponent } from './MultiWindowEntryComponent';
    2. @Component
    3. export struct TopTitleBar {
    4. // ...
    5. @State textModifier: TextModifier = new TextModifier();
    6. @State splitScreenWant: Want = {
    7. // Modify the bundleName, moduleName and abilityName of the current application,
    8. // and launch the UIAbility within the application.
    9. bundleName: 'com.example.multiticketclass',
    10. moduleName: 'multiticketclassdefaultsample',
    11. abilityName: 'SplitScreenAbility',
    12. };
    13. // ...
    14. build() {
    15. Row() {
    16. // ...
    17. // The area displayed by the icon on the right side
    18. Row({ space: 16 }) {
    19. // split screen
    20. Row() {
    21. MultiWindowEntryComponent({
    22. textModifier: this.textModifier,
    23. want: this.splitScreenWant
    24. })
    25. }
    26. .visibility(this.getMultiWindowVisibility())
    27. }
    28. }
    29. // ...
    30. }
    31. }

应用内分屏高阶组件窗口路由方案

建议开发者采用应用级多实例来实现分屏页面的路由管理。以下是页面级多实例与应用级多实例的主要区别,多股比价场景的分屏路由管理采用应用级多实例:

展开

场景

路由栈特点

是否需要路由改造

核心方案

页面级多实例

每个UI Ability创建后,基于当前节点改造路由栈

需要

以当前路由节点生成路由表,开发者手动定义路由方案

应用级多实例(推荐

每个UI Ability创建独立的相同路由栈

不需要

每个窗口启动时创建独立路由栈(路由表相同)

应用内分屏高阶组件窗口路由退栈方案

在多股比价场景中,当在应用内进行分屏操作时,新增窗口应保留当前浏览的股票信息,而主窗口则应回到股票列表。为实现这一功能,建议在新窗口的启动生命周期中触发事件,原窗口通过监听该事件并执行退栈操作。
  1. 在分屏程序的入口SplitScreenAbility.ets中的onCreate()和onNewWant()生命周期中进行事件触发。
    收起
    自动换行
    深色代码主题
    复制
    1. let eventData: emitter.EventData = {
    2. data: {
    3. 'isStart': 1,
    4. 'id': 1
    5. }
    6. };
    7. let innerEvent: emitter.InnerEvent = {
    8. eventId: 1,
    9. priority: emitter.EventPriority.HIGH
    10. };
    11. export default class SplitScreenAbility extends UIAbility {
    12. // ...
    13. onCreate(): void {
    14. // ...
    15. emitter.emit(innerEvent, eventData);
    16. }
    17. onNewWant(): void {
    18. // ...
    19. emitter.emit(innerEvent, eventData);
    20. }
    21. // ...
    22. }
  2. 在原窗口进行事件监听并做退栈处理。
    收起
    自动换行
    深色代码主题
    复制
    1. @Component
    2. export struct TopTitleBar {
    3. // ...
    4. private innerEvent: emitter.InnerEvent = { eventId: 1 };
    5. private callBack: Callback<emitter.EventData> = (eventData: emitter.EventData) => {
    6. Logger.info(`eventData:${eventData}`);
    7. if (this.pageInfos?.pop) {
    8. this.pageInfos.pop();
    9. }
    10. };
    11. aboutToAppear(): void {
    12. this.viewModel.loadData();
    13. if (this.context.abilityInfo.name === 'MultiticketclassdefaultAbility') {
    14. emitter.on(this.innerEvent, this.callBack);
    15. }
    16. }
    17. aboutToDisappear(): void {
    18. emitter.off(this.innerEvent.eventId, this.callBack);
    19. }
    20. // ...
    21. }
    22. }

应用内分屏高阶组件按钮显隐策略

应用内分屏高阶组件时,对不支持全景多窗的设备隐藏分屏按钮。方案的主要逻辑为:

  1. 监听窗口尺寸变化。
    收起
    自动换行
    深色代码主题
    复制
    1. public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
    2. this.mainWindowInfo.windowSize = windowSize;
    3. if (this.uiContext) {
    4. this.mainWindowInfo.widthBp = this.uiContext.getWindowWidthBreakpoint();
    5. this.mainWindowInfo.heightBp = this.uiContext.getWindowHeightBreakpoint();
    6. }
    7. };
    8. // ...
    9. updateWindowInfo(): void {
    10. try {
    11. // ...
    12. // Register for window size change monitoring, update window size and width/height breakpoint.
    13. this.mainWindow.on('windowSizeChange', this.onWindowSizeChange);
    14. // ...
    15. AppStorage.setOrCreate(KEY_MAIN_WINDOW_INFO, this.mainWindowInfo);
    16. } catch (error) {
    17. let err = error as BusinessError;
    18. Logger.error(`Failed to update window info. Code: ${err.code}, message: ${err.message}`);
    19. }
    20. }
  2. 尺寸变化时获取按钮节点,查询其enabled属性。
    收起
    自动换行
    深色代码主题
    复制
    1. private timerId: number = -1;
    2. aboutToAppear(): void {
    3. this.checkMultiWindowEnabled();
    4. }
    5. aboutToDisappear(): void {
    6. if (this.timerId !== -1) {
    7. clearTimeout(this.timerId);
    8. }
    9. }
    10. private checkMultiWindowEnabled(): void {
    11. this.timerId = setTimeout(() => {
    12. const frameNode = this.getUIContext()?.getFrameNodeById('MultiWindowEntryInAPP');
    13. const inspectorInfo = JSON.stringify(frameNode?.getInspectorInfo() as InspectorInfo);
    14. if (inspectorInfo?.search('"enabled":true') && inspectorInfo?.search('"enabled":true') !== -1) {
    15. this.isShowMultiWindowEntry = true;
    16. } else {
    17. this.isShowMultiWindowEntry = false;
    18. }
    19. }) as number;
    20. }
  3. 根据enabled属性通过visibility控制组件的显隐。
    收起
    自动换行
    深色代码主题
    复制
    1. Row() {
    2. MultiWindowEntryInAPP({
    3. want: this.want,
    4. isShowSubtitle: false,
    5. multiWindowEntryInAPPStyle: {
    6. // ...
    7. }
    8. })
    9. .id('MultiWindowEntryInAPP')
    10. }
    11. .visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)

电脑端页面

本章介绍如何基于现有移动端界面开发方案,实现代码逻辑与布局复用,高效完成股票类应用在电脑设备上的界面开发。

窗口适配

  • 窗口模式

    长视频应用在电脑端上支持全屏和自由窗口两种模式,具体实现可参考窗口模式。应用内监听窗口尺寸变化,通过断点刷新UI,即可自动适配全屏和自由窗口模式下的布局。

  • 窗口沉浸式

    根据UX设计规范,需要实现全屏和自由窗口下的沉浸式效果,具体实现可参考窗口沉浸式。全屏模式下,通过window.setWindowLayoutFullscreen()实现沉浸式。自由窗口模式下,通过window.setWindowDecorVisible(false)隐藏标题栏,仅保留右上角三键,使页面内容延伸至标题栏区域,实现沉浸式显示效果。

自选股页面

页面布局

  • 将电脑端自选股页划分为四个部分,效果图如下:

  • 对各个区域使用的多种能力进行分析,实现方案如下表:
    展开

    区域编号

    简介

    实现方案

    1

    侧边页签

    同移动端自选股页面对应区域的布局实现方案一致。

    2

    指数

    3

    股票列表-工具栏

    4

    股票列表

股票详情页

页面布局

  • 将电脑端股票详情页划分为五个部分,效果图如下:

  • 对各区域使用的能力进行分析,实现方案如表所示:
    展开

    区域编号

    简介

    实现方案

    1

    交易操作行

    同移动端股票详情页对应区域的布局实现方案一致。

    2

    标题

    3

    行情列表数据

    4

    中间Tab

    5

    曲线图和柱状图

在 最佳实践 中进行搜索
请输入您想要搜索的关键词