DEV Community

HarmonyOS
HarmonyOS

Posted on

ArkUI ImageSpan Best Practices: Inline Comment Badges & OOM-Safe Sizing

Read the original article:ArkUI ImageSpan Best Practices: Inline Comment Badges & OOM-Safe Sizing

ArkUI ImageSpan Best Practices: Inline Comment Badges & OOM-Safe Sizing

Requirement Description

Show a comment count (e.g., “11”) with a circular badge behind it, rendered inline with text.

Background Knowledge

  • Span: inline text inside Text/ContainerSpan.

  • ImageSpan: inline image inside Text/ContainerSpan.

Docs:

Implementation Steps

  1. Use a hollow (transparent-center) PNG for the badge.
  2. Render number + badge inline: Span(' 11 ') then ImageSpan(...).
  3. Overlap with a negative left margin so the badge sits under the number.
  4. Pick a fixed vp size (phones ~24–28vp; watches ~16–22vp).
  5. Accessibility: the number stays text; the badge is decorative.

Code Snippet / Configuration

import { LengthUnit, LengthMetrics } from '@kit.ArkUI';

@Entry
@Component
struct WearableCommentBadgeSimple {
  private badgeRes: Resource = $r('app.media.comment_badge_hollow')

  build() {
    Column({ space: 20 }) {
      Text('Wearable Inline Badge Simple')
        .fontSize('16fp')
        .fontWeight(FontWeight.Medium)
        .lineHeight('22vp')
        .textAlign(TextAlign.Center)
        .width('50%')

      this.badgeRowSmall('Comments', '11')
      this.badgeRowLarge('Activity', '100')

    }
    .padding(12)
    .alignItems(HorizontalAlign.Center)
    .backgroundColor("#ff736868")
    .width('100%')
    .height('100%')
  }

  @Builder
  private badgeRowSmall(label: string, count: string) {
    Row({ space: 8 }) {
      Text(label)
        .fontSize('13fp')
        .fontColor('#B0B0B0')
        .lineHeight('20vp')

      Text() {
        Span(`  ${count}  `)
          .fontColor(Color.Black)

        ImageSpan(this.badgeRes)
          .width('30vp')
          .height('30vp')
          .baselineOffset(new LengthMetrics(-12, LengthUnit.VP))
          .margin({ left: -28, top: 0 })
      }
      .fontSize('12fp')
      .fontWeight(FontWeight.Medium)
      .lineHeight('22vp')

    }
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  private badgeRowLarge(label: string, count: string) {
    Row({ space: 8 }) {
      Text(label)
        .fontSize('13fp')
        .fontColor('#B0B0B0')
        .lineHeight('20vp')

      Text() {
        Span(`  ${count}  `)
          .fontColor(Color.Black)

        ImageSpan(this.badgeRes)
          .width('44vp')
          .height('44vp')
          .margin({ left: -43.5, top: 0 })
          .baselineOffset(new LengthMetrics(-18, LengthUnit.VP))

      }
      .fontSize('16fp')
      .fontWeight(FontWeight.Medium)
      .lineHeight('26vp')
    }
    .alignItems(VerticalAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

img401.png

Limitations or Considerations

Large image crash (root cause & fix): If you write

Text() { ImageSpan(item.imgSrc) }
Enter fullscreen mode Exit fullscreen mode

without size, ImageSpan uses the original bitmap dimensions, which can cause high memory usage and a crash for very large images.

Fix: always bound ImageSpan with explicit width/height (thumbnails ~80–160vp) or display big media with a normal Image outside of Text().

ImageSpan($r('app.media.BigImage'))
  .width('120vp')
  .height('120vp')
Enter fullscreen mode Exit fullscreen mode

Wearables: round screens & tighter memory—prefer smaller badges (16–22vp) and verify line height/overflow.

Multi-digit counts: adjust badge size or margin for “100+”.

img402.png

Related Documents or Links

Span

ImageSpan

Written by Bunyamin Eymen Alagoz

Top comments (0)