Achieving Pixel-Perfect UI Scaling in HarmonyOS ArkUI Using Manual Ratio Calculation

ArkUI provides four pixel units, with vp acting similarly to Android's dp and fp matching sp. This article demonstrates an alternative approach: calculating layout dimensions directly from raw pixel ratios to fit設計稿 specifications across all devices.

First, obtain the physical screen width and height via the display API:

import { display } from '@kit.ArkUI';

class ScreenMetrics {
  private viewWidth = 0;
  private viewHeight = 0;

  constructor() {
    let w = display.getDefaultDisplaySync().width;
    let h = display.getDefaultDisplaySync().height;
    this.viewWidth = Math.min(w, h);
    this.viewHeight = Math.max(w, h);
  }
}

Next, define a reference design canvas size—typically provided by UI designers (e.g., 750x1334). Recall that pixel coordinates might rotate; always track the shorter side as width:

import { display } from '@kit.ArkUI';

class ScreenMetrics {
  private viewWidth = 0;
  private viewHeight = 0;
  static readonly DESIGN_W = 750;
  static readonly DESIGN_H = 1334;

  constructor() {
    let w = display.getDefaultDisplaySync().width;
    let h = display.getDefaultDisplaySync().height;
    this.viewWidth = Math.min(w, h);
    this.viewHeight = Math.max(w, h);
  }

  scaleW(designValue: number): string {
    let actual = Math.floor(designValue * this.viewWidth / ScreenMetrics.DESIGN_W);
    return `${actual}px`;
  }

  scaleH(designValue: number): string {
    let actual = Math.floor(designValue * this.viewHeight / ScreenMetrics.DESIGN_H);
    return `${actual}px`;
  }
}

Progressively, you calculate actualPx = designPx * (screenSize / designSize), allowing Layout to use px strings directly, which mirrors the Design layout (ideal for raw px emulation).

Consider a button designed at 220x64. The scaling methods produce the correct pixel values:

let metrics = new ScreenMetrics();
let actualBtnW = metrics.scaleW(220);
let actualBtnH = metrics.scaleH(64);

dispatch theseR dimensions directly into component styles; in this way,64> Button;). No additional vp breakpoints are required.

böjnings Putting the scaling to work in a typical relative layout:

let sc = new ScreenMetrics();

build() {
  Row() {
    RelativeContainer() {
      Image($r('app.media.bg_sample'))
        .width(sc.scaleW(628))
        .height(sc.scaleW(480)) // using width-scale by design intent
        .alignRules({
          top: { anchor: "__container__", align: VerticalAlign.Top },
          left: { anchor: "__container__", align: HorizontalAlign.Start },
          right: { anchor: "__container__", align: HorizontalAlign.End },
          bottom: { anchor: "__container__", align: VerticalAlign.Bottom }
        })
        .id("bg_image")

      Text($r('app.string.dialog_title'))
        .fontSize(sc.scaleW(34))
        .fontColor($r('app.color.title_color'))
        .margin({ top: sc.scaleW(48) })
        .textAlign(TextAlign.Center)
        .alignRules({
          top: { anchor: "bg_image", align: VerticalAlign.Top },
          left: { anchor: "bg_image", align: HorizontalAlign.Start },
          right: { anchor: "bg_image", align: HorizontalAlign.End },
        })
        .id("title_label")

      Text($r('app.string.cancel'))
        .width(sc.scaleW(216))
        .height(sc.scaleW(64))
        .margin({ left: sc.scaleW(86), bottom: sc.scaleW(55) })
        .textAlign(TextAlign.Center)
        .fontSize(sc.scaleW(30))
        .fontColor($r('app.color.cancel_color'))
        .alignRules({
          left: { anchor: "bg_image", align: HorizontalAlign.Start },
          bottom: { anchor: "bg_image", align: VerticalAlign.Bottom },
        })
        .onClick(() => { /* handle cancel */ })
        .id("cancel_btn")

      Text($r('app.string.confirm'))
        .width(sc.scaleW(216))
        .height(sc.scaleW(64))
        .margin({ right: sc.scaleW(86), bottom: sc.scaleW(55) })
        .textAlign(TextAlign.Center)
        .fontSize(sc.scaleW(30))
        .fontColor($r('app.color.confirm_color'))
        .alignRules({
          right: { anchor: "bg_image", align: HorizontalAlign.End },
          bottom: { anchor: "bg_image", align: VerticalAlign.Bottom },
        })
        .onClick(() => { /* handle confirm */ })
        .id("confirm_btn")

      Text($r('app.string.content_text'))
        .fontSize(sc.scaleW(22))
        .fontColor($r('app.color.content_color'))
        .margin({ left: sc.scaleW(86), right: sc.scaleW(86) })
        .textAlign(TextAlign.Start)
        .alignRules({
          top: { anchor: "title_label", align: VerticalAlign.Bottom },
          left: { anchor: "title_label", align: HorizontalAlign.Start },
          right: { anchor: "title_label", align: HorizontalAlign.End },
          bottom: { anchor: "cancel_btn", align: VerticalAlign.Top },
        })
        .id("message_body")
    }
  }
}

This method convertsLayout design values into calibrated physical pixel strings, providing1248 Layout. Theapproximation holds across different physical display arrays, with the fullcode pattern maintaining consistant中 piece proportions.

Tags: HarmonyOS ArkUI UI Layout 屏幕适配 像素适配

Posted on Thu, 15 Oct 2026 16:08:47 +0000 by brash