ArkUI 沉浸式布局:别让状态栏把页面顶得难看

举报
蓝瘦的蜕变 发表于 2026/08/14 17:55:29 2026/08/14
【摘要】 今天我们用一个小页面把沉浸式顶部讲清楚:背景可以铺进状态栏区域,但文字和按钮别跟着挤上去。 沉浸式布局最容易被误解成“把所有东西都顶到屏幕最上面”。这其实很危险。真正该上去的是背景、图片、色块这类装饰层;标题、按钮、正文这些可读内容,还是要留出安全距离。 第一块先看普通顶部布局。它规整、稳妥,适合大多数工具页。但如果设

ArkUI 沉浸式布局:别让状态栏把页面顶得难看

今天我们用一个小页面把沉浸式顶部讲清楚:背景可以铺进状态栏区域,但文字和按钮别跟着挤上去。

沉浸式布局最容易被误解成“把所有东西都顶到屏幕最上面”。这其实很危险。真正该上去的是背景、图片、色块这类装饰层;标题、按钮、正文这些可读内容,还是要留出安全距离。

第一块先看普通顶部布局。它规整、稳妥,适合大多数工具页。但如果设计稿希望顶部背景连到系统栏区域,这种写法会像被截了一刀。

第二块用 expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])。我们只放开顶部系统区域,让背景延伸上去。这里特意只处理 TOP,不把底部导航、键盘区域一起扯进来。

第三块用 safeAreaPadding(...) 做内容保护。背景可以沉浸,内容不能乱贴边。这个边界先想清楚,页面就不会为了“好看”牺牲可读性。

你自己做详情页或者首页 Banner 时,会更偏向普通顶部,还是沉浸式顶部?如果做沉浸式,你最担心的是适配问题,还是内容被遮住?

完整代码

class ImmersiveTip {
  title: string
  desc: string
  color: string

  constructor(title: string, desc: string, color: string) {
    this.title = title
    this.desc = desc
    this.color = color
  }
}

@Entry
@Component
struct ArkUIImmersiveLayoutDemo {
  private tips: ImmersiveTip[] = [
    new ImmersiveTip('背景可以进安全区', '让顶部色块或图片延伸上去,页面会更完整。', '#2563EB'),
    new ImmersiveTip('文字不要贴边', '标题、按钮这类可读内容仍然要留出安全距离。', '#0F766E'),
    new ImmersiveTip('只处理需要的边', '本篇只演示顶部,避免把底部导航和键盘混进来。', '#C2410C')
  ]

  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 6 }) {
      Text(title)
        .fontSize(19)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text(desc)
        .fontSize(13)
        .fontColor('#6B7280')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  normalHeaderPreview() {
    Column() {
      Row() {
        Text('普通顶部')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#111827')
        Blank()
        Text('安全区内')
          .fontSize(12)
          .fontColor('#6B7280')
      }
      .width('100%')
      .height(58)
      .padding({ left: 14, right: 14 })
      .backgroundColor('#F3F4F6')

      Column({ space: 8 }) {
        Text('顶部背景从安全区下方开始')
          .fontSize(15)
          .fontWeight(FontWeight.Medium)
          .fontColor('#374151')
        Text('这种写法规整,但如果设计稿希望顶部色块铺到状态栏区域,视觉上会显得被截了一刀。')
          .fontSize(13)
          .fontColor('#6B7280')
          .lineHeight(20)
      }
      .width('100%')
      .padding(14)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .borderRadius(14)
    .backgroundColor('#FFFFFF')
    .border({ width: 1, color: '#E5E7EB' })
  }

  @Builder
  immersiveHeaderPreview() {
    Column() {
      Column({ space: 12 }) {
        Text('沉浸式顶部')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFFFFF')
        Text('背景延伸到顶部非安全区,内容再用留白保护起来。')
          .fontSize(14)
          .fontColor('#DBEAFE')
          .lineHeight(22)
        Row({ space: 8 }) {
          Text('expandSafeArea')
            .fontSize(12)
            .fontColor('#0F172A')
            .padding({ left: 10, right: 10, top: 6, bottom: 6 })
            .borderRadius(14)
            .backgroundColor('#BFDBFE')
          Text('SafeAreaEdge.TOP')
            .fontSize(12)
            .fontColor('#0F172A')
            .padding({ left: 10, right: 10, top: 6, bottom: 6 })
            .borderRadius(14)
            .backgroundColor('#A7F3D0')
        }
      }
      .width('100%')
      .height(188)
      .padding({ left: 16, right: 16, top: 38, bottom: 18 })
      .borderRadius({ topLeft: 14, topRight: 14 })
      .backgroundColor('#1D4ED8')
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Start)
      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])

      Column({ space: 8 }) {
        Text('内容区继续正常排布')
          .fontSize(15)
          .fontWeight(FontWeight.Medium)
          .fontColor('#111827')
        Text('沉浸式不是让所有东西都顶上去,而是让背景更完整,内容仍然保持可读距离。')
          .fontSize(13)
          .fontColor('#6B7280')
          .lineHeight(20)
      }
      .width('100%')
      .padding(14)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .borderRadius(14)
    .backgroundColor('#FFFFFF')
    .border({ width: 1, color: '#BFDBFE' })
  }

  @Builder
  tipRow(item: ImmersiveTip) {
    Row({ space: 12 }) {
      Text(item.title.substring(0, 1))
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
        .textAlign(TextAlign.Center)
        .width(38)
        .height(38)
        .borderRadius(19)
        .backgroundColor(item.color)

      Column({ space: 4 }) {
        Text(item.title)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#111827')
        Text(item.desc)
          .fontSize(13)
          .fontColor('#4B5563')
          .lineHeight(20)
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(12)
    .borderRadius(10)
    .backgroundColor('#F8FAFC')
    .border({ width: 1, color: '#E5E7EB' })
  }

  @Builder
  protectedContentPreview() {
    Column({ space: 12 }) {
      Text('内容保护区')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text('safeAreaPadding 可以把安全区距离补回内容层。背景可以沉浸,文字和按钮不要硬贴系统区域。')
        .fontSize(13)
        .fontColor('#4B5563')
        .lineHeight(20)
      Column({ space: 10 }) {
        ForEach(this.tips, (item: ImmersiveTip) => {
          this.tipRow(item)
        }, (item: ImmersiveTip) => item.title)
      }
      .width('100%')
    }
    .width('100%')
    .padding({ left: 14, right: 14 })
    .safeAreaPadding({ top: 12, bottom: 12 })
    .borderRadius(14)
    .backgroundColor('#ECFDF5')
    .border({ width: 1, color: '#A7F3D0' })
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Scroll() {
      Column({ space: 22 }) {
        Column({ space: 8 }) {
          Text('ArkUI 沉浸式布局')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text('别让状态栏把页面顶得难看')
            .fontSize(16)
            .fontColor('#4B5563')
          Text('沉浸式布局的重点不是把内容塞进系统区域,而是让背景延伸过去,再把真正要读的内容保护好。')
            .fontSize(14)
            .fontColor('#6B7280')
            .lineHeight(22)
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('普通顶部布局:规整,但容易被截断', '常规顶部从安全区下方开始,适合大多数工具页。')
        this.normalHeaderPreview()

        this.sectionTitle('沉浸式顶部:让背景先铺上去', 'expandSafeArea 只放开需要的边,这里只演示顶部系统区域。')
        this.immersiveHeaderPreview()

        this.sectionTitle('内容保护:背景可以上去,文字别跟着挤上去', 'safeAreaPadding 负责把可读内容和系统区域隔开。')
        this.protectedContentPreview()
      }
      .width('100%')
      .padding({ left: 18, right: 18, top: 22, bottom: 32 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。