HarmonyOS 股票类应用多设备开发实战:从自选股到多股比价完整方案
这篇文章介绍了股票类应用的多端适配开发方案。主要内容包括:1)UX设计方面,展示了自选股页面、个股详情页和交易弹窗在不同设备上的布局方案;2)工程管理方面,提出了基于HarmonyOS的三层架构(公共能力层、基础特性层、产品定制层)的目录划分建议;3)窗口适配方面,说明了全屏、分屏、悬浮窗等模式的实现方法;4)具体开发实现,详细讲解了自选股页面和个股详情页的组件布局和交互设计。文章通过股票应用案例
一、这玩意儿是啥
股票类应用的目的是让用户更加便捷地办理金融业务。常见的有银行理财、股票、基金等类型的应用和业务场景,核心场景有数据查看、股票交易等。
股票类应用有以下特点:
丰富的信息聚合。
图表数据高效展示。
便捷高效的交互方式。
此类型的应用在多端设备的使用过程中,不仅要保障用户在办理金融业务的过程中正常使用,还要尽可能提升大屏幕的交互效率。
本文以目前流行的垂类市场中的股票类应用作为典型案例,详细介绍"一多"在实际开发中的应用,主要涵盖自选股和个股详情两个典型页面,展示其在直板机、双折叠、三折叠、阔折叠、平板五种产品形态上的"一次开发,多端部署"。
二、UX咋设计
以下是股票界面在直板机、阔折叠、双折叠设备及平板上的UX设计图。
自选股页面
页面主要包含:页面底部/侧边页签、标题、股票指数、股票列表-工具栏、股票列表。

个股详情页
页面主要包含:顶部标题、行情列表数据、分时Tab、曲线图和柱状图以及股票实时交易列表、讨论Tab、底部交易操作行。

半模态-股票交易弹窗

断点设计
股票类应用通常用于直板机、双折叠、三折叠、阔折叠和平板设备。以下为这些常用设备和屏幕尺寸(横向/纵向断点)的股票类应用适配策略:
阔折叠外屏(sm/md)、直板机(sm/lg):基础视图。
双折叠展开态、阔折叠横向展开(md):相较于直板机,双折叠在展开状态下,开发者可利用更大的屏幕空间扩展股票图表显示内容,以增强应用的沉浸感和信息显示,例如股票详情、股票K线图。
平板、三折叠展开态(lg):相较于直板机,平板设备在横向模式下能够显示更多内容。默认为双栏显示,左侧栏主要用于显示股票列表,右侧栏则展示股票详情信息,例如股票K线图。当点击全屏按钮时,页面切换至单栏显示,页面主要展示股票详情信息,此时股票K线图会占据更大比例,显示内容更加丰富。
三、工程咋管理
在创建"一多"工程时,开发者会面临工程结构目录的划分问题。考虑到复用性和可维护性,本文以股票类应用为例,提供推荐的参考方案。
HarmonyOS的分层架构包括产品定制层、基础特性层和公共能力层,为开发者提供清晰、高效、可扩展的设计架构。
股票类应用根据一多推荐的commons、features、products的"三层工程架构"划分目录。其中四个页面功能不同,互不依赖,根据页面划分为两个features(基础特性层):首页-home、股票详情页-stockdetail。公共常量、媒体播放工具以及窗口管理工具等需要被不同页面依赖引用的内容,划分为一个commons(公共能力层):基础能力-base。products(产品定制层)定制了程序标准启动流程和多场景协同场景的入口能力。
工程结构如下:
├──commons
│ └──base/src/main/ets
│ ├──constants // 公共常量
│ └──utils // 公共工具
├──features
│ ├──home/src/main
│ │ ├──ets
│ │ │ ├──models // 股票类数据
│ │ │ ├──pages // 应用首页内容
│ │ │ └──views // 首页视图组件
│ │ └──resources // 应用静态资源目录
│ └──stockdetail/src/main
│ ├──ets
│ │ ├──chartmodels // 图标组件
│ │ ├──models // 股票类数据
│ │ ├──pages // 股票详情页
│ │ └──views // 股票视图组件
│ └──resources // 应用静态资源目录
└──products
├──phone/src/main/ets
│ ├──entryability // 程序入口
│ ├──entrybackupability
│ ├──pages // 首页
│ ├──splitScreenAbility // 分屏入口
│ └──splitScreenBackupAbility
└──phone/src/main/resources // 应用静态资源目录
四、窗口咋适配
窗口模式
多设备股票类界面示例,根据适配的设备,涉及全屏模式、分屏模式、悬浮窗模式、自由窗口模式。其中分屏模式与悬浮窗通常无特殊设计,可通过系统方式进入。应用监听窗口尺寸变化,通过断点刷新UI,将自动适配全屏、分屏、悬浮窗、自由窗口模式下的布局。
使用系统UI组件进入全景多窗,实现一个应用多个窗口并行运行的体验,可参考功能开发:应用多实例-多股比价。
窗口方向
通过设置window.setPreferredOrientation()使应用跟随传感器自动旋转,可旋转至竖屏、横屏、反向竖屏、反向横屏四个方向。本示例使用跟随桌面的旋转模式。
窗口沉浸式
根据UX设计,实现不同窗口模式(全屏、分屏、悬浮窗)下窗口的沉浸式。全屏、分屏和悬浮窗的沉浸式均可通过setWindowLayoutFullscreen()实现,并进行动态安全区避让。
五、自选股页面咋开发
页面布局
将自选股页划分为四个部分,效果图如下:

对各个区域使用的多种能力进行分析,实现方案如下:
区域1:底部/侧边页签
借助响应式组件Tabs实现。
区域2:指数
最后一个组件固定,其他组件使用List组件实现延伸能力,随着设备宽度变大,页签间距变大,页面能够展示更多页签内容。
区域3:股票列表-工具栏
文字和功能按钮中间增加Blank组件,实现拉伸能力。
区域4:股票列表
通过使用List组件设置固定宽度和Scroll组件,可实现股票列表数据的上下或左右滑动。同时,支持对不同列设置不同的justifyContent,以便实现各列的不同对齐方式。
整个页面使用的是分栏布局,在股票列表区域,点击某一股票时,平板上会分栏显示该股票的详细信息。
交互开发
页签切换、自选股查看和跳转等交互均为简单的点击事件,开发过程可参考多设备交互。
六、股票详情页咋开发
页面布局
将个股详情页划分为六个部分,效果图如下:

对各区域使用的能力进行分析,实现方案如下:
区域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组件主动分屏或进入全景多窗。具体开发步骤如下:
导入模块:
import { MultiWindowEntryInAPP, MultiWindowEntryInAPPAttribute} from '@kit.UIDesignKit';
import { TextModifier } from '@kit.ArkUI';
import { Want } from '@kit.AbilityKit';
使用MultiWindowEntryInAPP组件,并且设置组件参数:
@Component
export struct MultiWindowEntryComponent {
@Link textModifier: TextModifier;
@Link want: Want;
@State isShowMultiWindowEntry: boolean = false;
// ...
build() {
Row() {
MultiWindowEntryInAPP({
want: this.want,
isShowSubtitle: false,
multiWindowEntryInAPPStyle: {
iconOptions: {
iconSize: 24,
iconColor: $r('sys.color.font_primary'),
iconWeight: FontWeight.Normal,
backgroundColor: $r('sys.color.comp_background_tertiary')
},
subtitleOptions: {
modifier: this.textModifier.fontColor(Color.Black)
}
}
})
.id("MultiWindowEntryInAPP")
}
.visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)
}
}
导入封装好的MultiWindowEntryComponent组件,并且设置组件参数:
import { MultiWindowEntryComponent } from './MultiWindowEntryComponent';
@Component
export struct TopTitleBar {
// ...
@State textModifier: TextModifier = new TextModifier();
@State splitScreenWant: Want = {
// Modify the bundleName, moduleName and abilityName of the current application, and launch the UIAbility within the application.
bundleName: 'com.example.multiticketclass',
moduleName: 'phone',
abilityName: 'SplitScreenAbility',
};
// ...
build() {
Row() {
// ...
// The area displayed by the icon on the right side
Row({ space: 16 }) {
// split screen
Row() {
MultiWindowEntryComponent({
textModifier: this.textModifier,
want: this.splitScreenWant
})
}
// ...
}
}
// ...
}
}
应用内分屏高阶组件窗口路由方案
建议开发者采用应用级多实例来实现分屏页面的路由管理。以下是页面级多实例与应用级多实例的主要区别,多股比价场景的分屏路由管理采用应用级多实例:
页面级多实例:
- 每个UI Ability创建后,基于当前节点改造路由栈
- 需要路由改造
- 以当前路由节点生成路由表,开发者手动定义路由方案
应用级多实例(推荐):
- 每个UI Ability创建独立的相同路由栈
- 不需要路由改造
- 每个窗口启动时创建独立路由栈(路由表相同)
应用内分屏高阶组件窗口路由退栈方案
在多股比价场景中,当在应用内进行分屏操作时,新增窗口应保留当前浏览的股票信息,而主窗口则应回到股票列表。为实现这一功能,建议在新窗口的启动生命周期中触发事件,原窗口通过监听该事件并执行退栈操作。
在分屏程序的入口SplitScreenAbility.ets中的onCreate()和onNewWant()生命周期中进行事件触发:
let eventData: emitter.EventData = {
data: {
'isStart': 1,
'id': 1
}
};
let innerEvent: emitter.InnerEvent = {
eventId: 1,
priority: emitter.EventPriority.HIGH
};
export default class SplitScreenAbility extends UIAbility {
// ...
onCreate(): void {
// ...
emitter.emit(innerEvent, eventData);
}
onNewWant(): void {
// ...
emitter.emit(innerEvent, eventData);
}
// ...
}
在原窗口进行事件监听并做退栈处理:
@Component
export struct TopTitleBar {
// ...
private innerEvent: emitter.InnerEvent = { eventId: 1 };
private callBack: Callback<emitter.EventData> = (eventData: emitter.EventData) => {
Logger.info(`eventData:${eventData}`);
if (this.pageInfos?.pop) {
this.pageInfos.pop();
}
};
aboutToAppear(): void {
if (this.context.abilityInfo.name === 'EntryAbility') {
emitter.on(this.innerEvent, this.callBack);
}
}
dispose(): void {
emitter.off(this.innerEvent.eventId, this.callBack);
}
// ...
}
}
应用内分屏高阶组件按钮显隐策略
在应用内分屏高阶组件时,对不支持全景多窗的设备隐藏分屏按钮。方案的主要逻辑为:
监听窗口尺寸变化:
public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
this.mainWindowInfo.windowSize = windowSize;
this.mainWindowInfo.widthBp = this.uiContext!.getWindowWidthBreakpoint();
this.mainWindowInfo.heightBp = this.uiContext!.getWindowHeightBreakpoint();
};
// ...
updateWindowInfo(): void {
try {
// ...
// Register for window size change monitoring, update window size and width/height breakpoint.
this.mainWindow.on('windowSizeChange', this.onWindowSizeChange);
// ...
AppStorage.setOrCreate('mainWindowInfo', this.mainWindowInfo);
} catch (error) {
let err = error as BusinessError;
hilog.error(0x0000, `TestLog`, `Failed to update window info. Code: ${err.code}, message: ${err.message}`);
}
}
尺寸变化时获取按钮节点,查询其enabled属性:
@StorageLink('mainWindowInfo') @Watch('watchWindow') mainWindowInfo: WindowInfo = new WindowInfo();
aboutToAppear(): void {
this.watchWindow();
}
private watchWindow(): void {
setTimeout(()=> {
let nodeStr = JSON.stringify(this.getUIContext()?.getFrameNodeById("MultiWindowEntryInAPP")?.getInspectorInfo());
if (nodeStr?.search('"enabled":true') && nodeStr?.search('"enabled":true') !== -1) {
this.isShowMultiWindowEntry = true;
} else {
this.isShowMultiWindowEntry = false;
}
})
}
根据enabled属性通过visibility控制组件的显隐:
Row() {
MultiWindowEntryInAPP({
want: this.want,
isShowSubtitle: false,
multiWindowEntryInAPPStyle: {
// ...
}
})
.id("MultiWindowEntryInAPP")
}
.visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)
八、避坑指南
坑1:分屏按钮显隐
对不支持全景多窗的设备,分屏按钮应该隐藏,不能点击。
咋解决?监听窗口尺寸变化,获取按钮节点的enabled属性,通过visibility控制组件的显隐。
坑2:分屏窗口路由退栈
在应用内进行分屏操作时,新增窗口应保留当前浏览的股票信息,而主窗口则应回到股票列表。
咋解决?在新窗口的启动生命周期中触发事件,原窗口通过监听该事件并执行退栈操作。
坑3:应用级多实例 vs 页面级多实例
页面级多实例需要路由改造,开发者手动定义路由方案;应用级多实例不需要路由改造,每个窗口启动时创建独立路由栈。
咋解决?建议开发者采用应用级多实例来实现分屏页面的路由管理,减少开发工作量。
坑4:MultiWindowEntryInAPP组件约束
MultiWindowEntryInAPP组件依赖全景多窗特性,只有当前设备及屏幕状态支持全景多窗,才支持设置此功能。
咋解决?对于不支持的设备型态,该组件不可交互,不响应点击事件,需要通过enabled属性判断并控制显隐。
坑5:股票列表布局
股票列表需要支持对不同列设置不同的justifyContent,以便实现各列的不同对齐方式。
咋解决?通过使用List组件设置固定宽度和Scroll组件,可实现股票列表数据的上下或左右滑动,并支持自定义对齐方式。
坑6:行情列表数据自适应
行情列表数据需要根据不同断点自适应两列变多列。
咋解决?通过栅格布局并结合断点,控制在不同断点下显示不同的列数。
九、总结
股票类应用的多设备开发,从UX设计、工程管理、窗口适配、界面开发、功能开发等方面,都有值得学习的最佳实践。
UX设计方面,股票类应用的特点是丰富的信息聚合、图表数据高效展示、便捷高效的交互方式。在不同设备上,要保障用户正常使用,还要尽可能提升大屏幕的交互效率。
工程管理方面,三层架构划分清晰,commons、features、products各司其职,便于维护和复用。特别是分屏入口(splitScreenAbility)的设计,为多股比价场景提供了基础。
窗口适配方面,涉及全屏模式、分屏模式、悬浮窗模式、自由窗口模式。应用监听窗口尺寸变化,通过断点刷新UI,自动适配不同模式下的布局。
界面开发方面,自选股页面使用分栏布局,股票详情页使用栅格布局、List组件、layoutWeight等多种能力。关键是根据不同断点设置不同的属性值,实现自适应布局。
功能开发方面,应用多实例-多股比价是一个亮点。通过MultiWindowEntryInAPP组件,实现分屏(双股对比)和全景多窗(三股对比),提升大屏设备的交互效率。
最后,股票类应用的核心是金融业务的便捷性和图表数据的展示效率。开发时要多关注这些细节,才能做出好的产品。
更多推荐

所有评论(0)