目录

Stock

Overview

This topic uses stock apps as a typical example to detail how the feature of one-time development for multi-device deployment is applied in actual development. The following describes how to develop two typical pages (stock watchlist page and individual stock details page) on bar phones, bi-fold phones, tri-fold phones, widescreen foldable phones, tablets, and PCs. The following practices will focus on these product forms and provide examples from the perspectives of UX design, project management, and page development for mobile devices and PCs, helping you develop stock apps.

Before reading this topic, you are advised to learn the ArkUI framework and the capabilities of one-time development for multi-device deployment.

The following practices will focus on these product forms and provide examples from the perspectives of UX design, project management, and page development, helping you develop stock apps that can be deployed on multiple devices.

UX Design

Stock apps are designed to enable users to handle financial services more conveniently. Common scenarios include wealth management, stock, and fund. Core scenarios include data viewing and stock transaction.

Stock apps have the following characteristics:

When used across devices, stock apps need to ensure basic user experience and access to all necessary functions when handling financial services for users, and improve the interaction efficiency on large-screen devices.

For details about the UX design of stock apps, see Self-Selected Stock in the multi-device responsive design guide for financial services. See the following figure for reference.

Project Management

[h2]Project Creation

You are advised to create a template project after learning how to create and configure a layered architecture project by referring to Cross-Device Project Deployment and Release. Then, modify the project based on the development requirements of the stock app to ensure that the project architecture meets the actual service requirements.

[h2]Project Structure

When creating a project, you may encounter the problem of how to divide the project structure directory. Considering the reusability and maintainability of the project, this topic uses the stock app as an example to provide a recommended solution for reference.

The HarmonyOS project architecture uses a layered architecture consisting of three layers: product customization layer, basic feature layer, and common capability layer. It provides a clear, efficient, and scalable design architecture. For details, see Layered Architecture Design.

The stock app organizes its directories based on the three-layer project architecture of common (common capability layer), features (basic feature layer), and products (product customization layer). The features layer is divided into three independent modules based on service functions: stockdeal (including the stock purchase dialog, K-line chart, and transaction details components), stockdetail (including the stock details page, multi-windows entry, and information table), and stockmarket (including the market list component). Base capabilities (base), which are referenced by different pages, such as common constants, media playback tools, and window management tools, are included in the common layer. The products layer includes the default and pc modules, which customizes the standard startup process and the entry capability in collaborative scenarios.

The project structure consists of the following directory levels:
├──commons
│  └──base/src/main
│     └──ets
│        ├──baseviews                     // Common view components
│        ├──models                        // Common data models
│        └──utils                         // Common utilities
├──features
│  ├──stockdeal/src/main
│  │  └──ets
│  │     ├──chartmodels                   // Chart models
│  │     ├──models                        // Stock transaction data models
│  │     ├──viewmodels                    // Stock transaction view models
│  │     └──views                         // Stock trading view components
│  ├──stockdetail/src/main
│  │  ├──ets
│  │  │  ├──models                        // Stock details data models
│  │  │  ├──viewmodels                    // Stock details view models
│  │  │  ├──pages                         // Stock details page
│  │  │  └──views                         // Stock details view components
│  └──stockmarket/src/main
│     └──ets
│        ├──models                        // Stock market data models
│        ├──viewmodels                    // Stock market view models
│        └──views                         // Stock market view components
└──products
   ├──default/src/main
   │  ├──ets
   │  │  ├──entryability                  // App entry on the mobile client
   │  │  ├──entrybackupability            // Backup entry class
   │  │  ├──pages                         // Home page on the mobile client
   │  │  ├──splitScreenAbility            // Split-screen ability
   │  │  └──splitScreenBackupAbility      // Split-screen backup ability
   │  └──resources                        // Static resources
   └──pc/src/main
      ├──ets
      │  ├──pages                         // PC pages
      │  ├──pcability                     // PC program entry
      │  └──pcbackupability               // PC program backup entry
      └──resources                        // Static resources

Mobile Pages

[h2]Window Adaptation

Window Mode

Multi-Device Stock Page provides the full-screen, split-screen, floating window, and freeform window modes for different devices. For details, see Window Mode. The split-screen and floating window modes can be easily accessed through the system. By monitoring window size changes, the app refreshes the UI based on the breakpoints to automatically apply layouts suitable for distinct window modes, such as the full-screen, split-screen, floating window, and freeform window modes.

Multitask view, which is accessed via system UI components, allows an app to run in multiple windows at the same time. For details, see Feature Development: Multi-Stock Price Comparison in Stock Details Page.

Window Orientation

Set setPreferredOrientation() to enable the app to automatically rotate with the sensor. On bar-type phones, only the portrait mode is recommended. On large screens, such as bi-fold (unfolded), tri-fold (G state), and tablets, rotation in four orientations controlled by the rotation switch in Control Panel is recommended. In a stock app, you are advised to set this attribute to FOLLOW_DESKTOP in the module.json5 file. For details, see Window Orientation.

Immersive Window

You can implement the immersive windows in different modes (full-screen, split-screen, and floating window modes) based on the UX design. For details, see Immersive Window. You can implement the immersive windows in full-screen, split-screen, and floating window modes using setWindowLayoutFullscreen() and dynamically avoid the safe area.

[h2]Stock Watchlist Page

The stock watchlist page responds to user input, displays stock indexes and watchlist information, and redirects to the stock details page. Based on the function design, the stock watchlist page is divided into four areas, as described in the following table.

Horizontal/Vertical Breakpoint

sm/md

sm/lg

md

lg

Stock watchlist page

UI Development

The following table describes the implementation solutions for each area.

No.

Description

Implementation

1

Bottom/Side tab

The responsive component HdsTabs is used. In addition, the Tabs component is used on devices with earlier API versions.

2

Stock index

The last component is fixed. The List component is used to implement the extension capability for other components. As the device width increases, a page can show more content.

3

Stock list - toolbar

The Blank component is added between the text and feature buttons to implement the stretching capability.

4

Stock list

The List component is used to set a fixed width and the Scroll component is used to enable the stock list to be scrollable. In addition, different justifyContent values can be set for various columns to implement distinct alignment modes.

[h2]Stock Details Page

The stock details page responds to user input and displays detailed information about a stock and discussions. Based on the function design, the stock watchlist page is divided into six areas, as described in the following table.

Horizontal/Vertical Breakpoint

sm/md

sm/lg

md

lg, xl

Individual stock details page

UI Development

The following table describes the implementation solutions for each area.

No.

Description

How to Implement

1

Transaction operation bar

The layoutWeight attribute is set for the transaction button and the Blank component is used with breakpoints to implement adaptive stretching of the button.

2

Title

The title is displayed in the center and the operation buttons are justified. The Blank component is used for the blank space to implement the stretching capability of the adaptive layout.

3

Trend list

The Grid component is used with breakpoints to control the number of columns displayed at different breakpoints. The list can change from two columns to multiple columns adaptively.

4

Middle tab

The space attribute of the List component is used with breakpoints to control the spacing between list items at different breakpoints.

5

Line chart and bar chart

The layoutWeight attribute is used to implement the stretching capability.

6

Discussion tab

The space attribute of the List component is used with breakpoints to control the spacing between list items at different breakpoints.

Feature Development: Multi-Stock Price Comparison

The app uses the system component MultiWindowEntryInAPP to configure its bundleName and UIAbility to be started (other apps cannot be started). Tapping the component displays a split-screen view for dual-stock comparison; tapping the component again based on the split-screen view displays a multitask view for triple-stock comparison.

The following uses Mate X5 (a foldable phone) as an example to show the display effect in split-screen and multitask views.

-

Split-Screen View for Dual-Stock Comparison

Multitask View for Triple-Stock Comparison

Individual stock details page - stock comparison

Constraints

The MultiWindowEntryInAPP component is applicable only when the current device and its screen state support the multitask view. Currently, the following device types support the multitask view:

For other device types or states, this component will not respond to any taps.

You are advised to set a close button in the upper left corner of the split window. In this example, a back button is used, which is a specific requirement for returning to the previous page in the stock comparison scenario.

How to Develop

The app uses the MultiWindowEntryInAPP component to proactively enter the split-screen view or multitask view. The development procedure is as follows:

  1. Import the related modules.
    import { MultiWindowEntryInAPP } from '@kit.UIDesignKit';
    import { TextModifier } from '@kit.ArkUI';
    import { Want } from '@kit.AbilityKit';
  2. Use the MultiWindowEntryInAPP component and set relevant parameters.
    @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($r('app.color.text_primary_color'))
              }
            }
          })
            .id('MultiWindowEntryInAPP')
        }
        .visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)
      }
    }
  3. Import the encapsulated MultiWindowEntryComponent and set component parameters.
    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: 'multiticketclassdefaultsample',
        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
              })
            }
            .visibility(this.getMultiWindowVisibility())
          }
        }
        // ...
      }
    }

Window Routing Solution for MultiWindowEntryInAPP

You are advised to use the app-level multi-instance to manage the routing of split-screen pages. The following table describes the differences between page-level multi-instance and app-level multi-instance. For split-screen routing management in the stock comparison scenario, the app-level multi-instance is used.

Scenario

Route Stack Feature

Route Reconstruction Required

Core Solutions

Page-level multi-instance

A UIAbility is created when the page loads, and the route stack is reconstructed based on the current node.

Yes

The route table is generated based on the current route node, and the route solution requires manual configuration.

App-level multi-instance (recommended)

Each UIAbility creates an independent and identical route stack.

No

An independent route stack (with the same route table) is created upon a window startup.

Route Stack Pop-out Solution for MultiWindowEntryInAPP

In the stock comparison scenario, when the app enters the split-screen view, the new window should retain the current stock information, and the main window should return to the stock list. To implement this functionality, you are advised to trigger an event in the startup lifecycle of the new window. The original window monitors the event and performs the pop-out operation.
  1. Trigger the event in the onCreate() and onNewWant() lifecycles of the app's entry SplitScreenAbility.ets.
    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);
      }
    
      // ...
    }
  2. Monitor the event in the original window and perform the pop-out operation.
    @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 {
        this.viewModel.loadData();
        if (this.context.abilityInfo.name === 'MultiticketclassdefaultAbility') {
          emitter.on(this.innerEvent, this.callBack);
        }
      }
    
      aboutToDisappear(): void {
        emitter.off(this.innerEvent.eventId, this.callBack);
      }
    
      // ...
      }
    }

Button Visibility Policy for MultiWindowEntryInAPP

Hide the split-screen button on devices that do not support multitask view for the MultiWindowEntryInAPP component. The main logic of the solution is as follows:

  1. Monitor window size changes.
    public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
      this.mainWindowInfo.windowSize = windowSize;
      if (this.uiContext) {
        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(KEY_MAIN_WINDOW_INFO, this.mainWindowInfo);
      } catch (error) {
        let err = error as BusinessError;
        Logger.error(`Failed to update window info. Code: ${err.code}, message: ${err.message}`);
      }
    }
  2. Obtain the button node and check its enabled attribute when the window size changes.
    private timerId: number = -1;
    
    aboutToAppear(): void {
      this.checkMultiWindowEnabled();
    }
    
    aboutToDisappear(): void {
      if (this.timerId !== -1) {
        clearTimeout(this.timerId);
      }
    }
    
    private checkMultiWindowEnabled(): void {
      this.timerId = setTimeout(() => {
        const frameNode = this.getUIContext()?.getFrameNodeById('MultiWindowEntryInAPP');
        const inspectorInfo = JSON.stringify(frameNode?.getInspectorInfo() as InspectorInfo);
        if (inspectorInfo?.search('"enabled":true') && inspectorInfo?.search('"enabled":true') !== -1) {
          this.isShowMultiWindowEntry = true;
        } else {
          this.isShowMultiWindowEntry = false;
        }
      }) as number;
    }
  3. Use the visibility attribute to control the visibility of the button based on the enabled attribute.
    Row() {
      MultiWindowEntryInAPP({
        want: this.want,
        isShowSubtitle: false,
        multiWindowEntryInAPPStyle: {
          // ...
        }
      })
        .id('MultiWindowEntryInAPP')
    }
    .visibility(this.isShowMultiWindowEntry ? Visibility.Visible : Visibility.None)

PC Pages

This section describes how to reuse the code logic and layout based on the existing mobile UI development solution to efficiently develop stock apps for PCs.

[h2]Window Adaptation

Sample Code