DEV Community

HarmonyOS
HarmonyOS

Posted on

How can dynamic interface changes be implemented within the same page in HarmonyOS Lite Wearable applications?

Read the original article:How can dynamic interface changes be implemented within the same page in HarmonyOS Lite Wearable applications?

Requirement Description

Although developing HarmonyOS Lite Wearable applications may seem relatively easy since they are built using HML, CSS, and JS, it can be challenging for developers compared to ArkTS, as state management cannot be implemented. For example, to enable dynamic changes within the same page, a similar logic must also be established in Lite Wearable applications.

Background Knowledge

  • HarmonyOS Lite Wearable Architecture: HarmonyOS Lite Wearable is a microkernel-based operating system optimized for low power consumption and limited hardware resources. This architecture enhances performance and energy efficiency, but also requires developers to work within constrained computational environments.
  • Development Languages and Framework: Lite Wearable applications are primarily developed using HML (Harmony Markup Language), CSS, and JavaScript. This structure resembles standard web technologies; however, it lacks advanced features such as integrated state management systems found in frameworks like React or languages like ArkTS.
  • State Management Limitation: In HML/JS-based projects, one-way data flow and reactive state management are not natively supported. As a result, developers must manually implement mechanisms to manage application state and trigger user interface updates dynamically.
  • ArkTS vs. Lite Wearable Development: ArkTS (Ark TypeScript), used in the full-featured HarmonyOS environment, provides modern development capabilities such as reactive data binding and built-in state management. In contrast, the Lite Wearable platform lacks these functionalities, requiring developers to rely on manual control and direct DOM manipulation to achieve similar behaviors.

Implementation Steps

The following implementation steps explain how dynamic interface changes can be applied within the same page in HarmonyOS Lite Wearable applications. This functionality, implemented using JS and HML, can be achieved through the following steps:

  1. State Variable Definition: A variable named currentScreen is defined in the JavaScript data object to control which interface section is displayed.
  2. Conditional Rendering in HML: Each interface layout is wrapped with an if="{{currentScreen === 'value'}}" condition in the HML file. This allows only the layout matching the current state to be rendered on the screen.
  3. State Update through Event Handlers: User actions such as accepting, declining, or ending a call trigger functions (acceptClick(), declineClick(), endClick()) that update the value of currentScreen.
  4. Dynamic UI Transition: Once currentScreen is updated, the visible interface automatically changes to the corresponding section without reloading or navigating to another page.

Code Snippet / Configuration

The following code blocks present an example of a fake call application in which dynamic interface changes are implemented within a single page. In this project, the incoming, accepting, and declining call states are all managed on the same page.

Index.html :

<div class="container">

    <div class="main-screen" if="{{currentScreen === 'incoming'}}">
        <text class="caller-name">Mehmet Algül</text>
        <text class="caller-number"></text>

        <div class="button-container">
            <div class="decline-btn" onclick="declineClick">
                <image src="../../common/callDecline.png" class="button-icon"></image>
            </div>
            <div class="accept-btn" onclick="acceptClick">
                <image src="../../common/callAccept.png" class="button-icon"></image>
            </div>
        </div>
    </div>

    <div class="main-screen" if="{{currentScreen === 'accept'}}">
        <text class="caller-name">Mehmet Algül</text>
        <text class="caller-number"></text>
        <text class="call-timer">{{callTimer}}</text>

        <div class="button-container">
            <div class="mic-btn">
                <image src="../../common/microphone.png" class="button-icon"></image>
            </div>
            <div class="close-btn" onclick="endClick">
                <image src="../../common/callClose.png" class="button-icon"></image>
            </div>
            <div class="speaker-btn">
                <image src="../../common/speaker.png" class="button-icon"></image>
            </div>
        </div>
    </div>

    <div class="main-screen" if="{{currentScreen === 'decline'}}">
        <text class="caller-name">Mehmet Algül</text>
        <text class="call-declined">Call Declined</text>
    </div>

    <div class="main-screen" if="{{currentScreen === 'end'}}">
        <text class="caller-name">Mehmet Algül</text>
        <text class="call-ended">Call Ended</text>
        <text class="call-timer">{{callTimer}}</text>

    </div>

</div
Enter fullscreen mode Exit fullscreen mode

This HML code defines the user interface for a fake call application with multiple call states, all managed on a single page. The container element wraps four main sections, each representing a different state of the call:

  1. Incoming Call Screen (currentScreen === 'incoming'): Displays the caller’s name and number along with two buttons for accepting or declining the call. The buttons trigger acceptClick and declineClick event handlers when tapped.
  2. Accepted Call Screen (currentScreen === 'accept'): Shows the caller information along with a live call timer (callTimer) and buttons for microphone, speaker, and ending the call. The endClick handler updates the interface to the call-ended state.
  3. Declined Call Screen (currentScreen === 'decline'): Displays the caller’s name and a “Call Declined” message, indicating that the call has been rejected.
  4. Ended Call Screen (currentScreen === 'end'): Shows the caller’s name, the final call duration, and a “Call Ended” message after the call has finished.

The visibility of each section is controlled dynamically using the if="{{currentScreen === 'value'}}" directive, allowing seamless transitions between states without navigating to separate pages.

Index.js :

import app from '@system.app';
import vibrator from '@system.vibrator';
export default {
    data: {
        currentScreen: 'incoming',
        callTimer: '00:00',
        timerInterval: null,
        seconds: 0,
        vibrationInterval: null
    },

    onInit() {
        this.currentScreen = 'incoming';
        this.startVibration();
    },

    acceptClick() {
        this.stopVibration();
        this.currentScreen = 'accept';
        this.startTimer();
    },

    declineClick() {
        this.stopVibration();
        this.currentScreen = 'decline';
        setTimeout(() => {
            app.terminate();
        }, 3000);
    },

    endClick() {
        this.stopVibration();
        this.currentScreen = 'end';
        this.stopTimer();
        setTimeout(() => {
            app.terminate();
        }, 3000);
    },

    startTimer() {
        this.seconds = 0;
        this.callTimer = '00:00';
        this.timerInterval = setInterval(() => {
            this.seconds++;
            this.formatTimer();
        }, 1000);
    },

    stopTimer() {
        if (this.timerInterval) {
            clearInterval(this.timerInterval);
            this.timerInterval = null;
        }
    },

    formatTimer() {
        const minutes = Math.floor(this.seconds / 60);
        const secs = this.seconds % 60;
        this.callTimer =
            (minutes < 10 ? '0' + minutes : minutes) + ':' +
                (secs < 10 ? '0' + secs : secs);
    },

    startVibration() {
        this.stopVibration()
        vibrator.vibrate({
            mode: 'short',
            success() {
                console.log('success to vibrate');
            },
            fail(data, code) {
                console.log(`handle fail, data = ${data}, code = ${code}`);
            },
        });

        this.vibrationInterval = setInterval(() => {
            vibrator.vibrate({
                mode: 'short',
                success() {
                    console.log('success to vibrate');
                },
                fail(data, code) {
                    console.log(`handle fail, data = ${data}, code = ${code}`);
                },
            });
        }, 2000);
    },

    stopVibration() {
        if (this.vibrationInterval !== undefined) {
            clearInterval(this.vibrationInterval);
            this.vibrationInterval = null;
            vibrator.vibrate({
                mode: "none",
                success: function() {
                    console.log('Vibration stopped');
                },
                fail: function(err) {
                    console.log('Vibration stop error: ' + err);
                }
            });
        }
    }
Enter fullscreen mode Exit fullscreen mode

This JavaScript code provides the functional logic for managing dynamic interface transitions and device interactions in the fake call application. The main points are as follows:

  1. State Management: The currentScreen variable in the data object controls which UI section is displayed. Changing its value dynamically updates the visible interface without navigating to a new page.
  2. Event Handlers: Functions such as acceptClick(), declineClick(), and endClick() handle user interactions. These functions update currentScreen to reflect the current call state and trigger additional actions such as starting/stopping timers and vibrations.
  3. Call Timer Functionality: startTimer() and stopTimer() manage the call duration. The timer updates every second and formats the elapsed time into a MM:SS display using the formatTimer() method.
  4. Vibration Control: The startVibration() function uses the @system.vibrator API to trigger periodic vibrations during an incoming call. The stopVibration() function stops the vibration, either when the call is accepted, declined, or ended.
  5. Application Termination: After declining or ending a call, the application waits for three seconds (setTimeout) before calling app.terminate(), allowing the user to briefly view the final call state.

This JS logic works in conjunction with the HML interface to enable dynamic UI changes on a single page, simulating a realistic call experience on HarmonyOS Lite Wearable devices.

Index.css :

.container {
  width: 100%;
  height: 100%;
  background-color: #000000;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.main-screen {
  width: 100%;
  height: 100%;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.caller-name {
  font-size: 32px;
  color: #ffffff;
  margin-bottom: 8px;
  text-align: center;
}

.call-timer {
  font-size: 20px;
  color: #ffffff;
  text-align: center;
  margin-bottom: 30px;
}

.call-declined, .call-ended {
  font-size: 24px;
  color: #ffffff;
  margin-bottom: 20px;
  text-align: center;
}


.button-container {
  flex-direction: row;
  justify-content: center;
  width: 100%;
  height: 20%;
  align-items: center;
}

.accept-btn, .decline-btn, .mic-btn, .close-btn, .speaker-btn {
  width: 30%;
  height: 100%;
  justify-content: center;
  align-items: center;
  background-color: transparent;
}

.button-icon {
  width: 80px;
  height: 80px;
}

.caller-number {
  font-size: 22px;
  color: #ffffff;
  margin-bottom: 20px;
  text-align: center;
Enter fullscreen mode Exit fullscreen mode

This CSS code contains the styling for the corresponding page.

  1. Container Layout (.container): The main container occupies the full width and height of the screen, centering its content both vertically and horizontally in a column layout.
  2. Screen Sections (.main-screen): Each screen section inherits the full screen size and centers its content, ensuring consistent alignment across different call states.
  3. Text Styling:
    • .caller-name and .caller-number display the caller information prominently.
    • .call-timer shows the call duration in a readable font size.
    • .call-declined and .call-ended indicate the call status clearly. All text elements use white color for contrast and have appropriate margins for spacing.
  4. Button Container (.button-container): Arranges interactive buttons in a horizontal row, centered on the screen, with defined height and width to maintain consistency.
  5. Individual Buttons (.accept-btn, .decline-btn, .mic-btn, .close-btn, .speaker-btn): Each button is sized uniformly and centered, with a transparent background to focus on the icon.
  6. Button Icons (.button-icon): Ensures that the images within buttons have consistent width and height for a uniform visual appearance.

Test Results

The following screenshots display images taken from the corresponding application.

image.pngimage.pngimage.pngimage.png

Limitations or Considerations

  • Limited State Management: HarmonyOS Lite Wearable applications developed with HML and JavaScript lack native reactive state management, which increases development complexity. Developers must manually implement state tracking and UI updates, which may introduce errors or inconsistencies in larger applications.
  • Performance Constraints: Lite Wearable devices have limited processing power and memory. Frequent UI updates or intensive JavaScript operations, such as timers and vibration control, may impact performance and battery life.
  • Screen Size Limitations: The small display area of wearable devices restricts the amount of information that can be presented simultaneously. Careful layout planning and concise UI elements are required to ensure readability and usability.
  • Manual UI Transitions: Since the platform does not support advanced component-based frameworks like ArkTS, all dynamic UI transitions must be manually controlled using conditional rendering and state updates, which can be time-consuming to scale for more complex applications.
  • Limited API Support: Certain device functionalities, such as vibration or advanced animations, rely on system APIs. Variations in API behavior across different Lite Wearable devices may require additional testing and handling to maintain consistency.
  • User Interaction Constraints: Interaction methods are limited to simple gestures and button taps. Complex interactions or multi-touch gestures are not fully supported, which may restrict the design of interactive features.

Related Documents or Links

https://developer.huawei.com/consumer/en/doc/harmonyos-references/arkui-js-lite-comp

https://developer.huawei.com/consumer/en/doc/harmonyos-references/arkui-declarative-comp

https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-system-vibrate

Written by Mehmet Algul

Top comments (0)