ArkUI 层叠布局(对照 Android):用 Stack 做封面图和底部浮层

上一篇讲 Row / Column,解决的是一组组件按横向或纵向排列的问题。实际页面里还有另一类结构:组件不是排成一行或一列,而是叠在同一块区域里

常见场景包括:

  • 图片底部压一层标题和说明。
  • 封面图上覆盖一个半透明遮罩。
  • 页面内容上方临时盖一个加载层或空状态层。
  • 单独一个角标贴在图片角落。

这类结构放到 ArkUI 里,优先看 Stack

本文只解决一个问题:如何用 Stack 搭一个“封面图 + 底部信息浮层”的内容卡片。 不覆盖:右上角角标 + 中间播放按钮 + 底部说明同时存在的复杂封面卡片。这种多控件、多锚点布局更适合作为后续 RelativeContainer 的例子:外层用 Stack 放封面图,内部控件再交给 RelativeContainer 处理相对定位。

先看要搭的 UI 结构

先定一个基础场景:一个课程/文章卡片。

它由两层组成:

Stack:整张封面区域
  Image:封面图,铺满底层
  Column:底部标题和说明,覆盖在图片下方

Row / Column 关心“沿哪个方向排”;Stack 关心“哪几层叠在同一块区域里,以及这一层默认贴到哪里”。

ArkUI:用 Stack 写封面卡片

先看 ArkUI 版本:

@Component
struct CourseCoverCard {
  build() {
    Stack({ alignContent: Alignment.BottomStart }) {
      Image($r('app.media.course_cover'))
        .width('100%')
        .height('100%')
        .objectFit(ImageFit.Cover)

      Column() {
        Text('ArkUI 布局入门')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
          .fontColor(Color.White)

        Text('Stack 适合底图、遮罩和底部信息浮层')
          .fontSize(12)
          .fontColor('#E5E7EB')
          .margin({ top: 4 })
      }
      .width('100%')
      .padding(12)
      .backgroundColor('#00000080')
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height(180)
    .borderRadius(16)
    .clip(true)
  }
}

实现效果:

Pasted image 20260524221612

这段代码里,Stack 管的是封面区域,两个子组件共享同一个 180vp 高度的容器。

  • Image 先写,作为底图。
  • ImageFit.Cover 让图片等比铺满,比例不一致时自动裁掉多余部分。
  • Column 后写,覆盖在图片上。
  • Stack({ alignContent: Alignment.BottomStart }) 把未单独定位的浮层放到底部左侧。
  • Column.width('100%') 让底部浮层横向铺满整张封面。

Stack 时可以先按这三个问题拆: 1. 底层是什么:图片、色块,还是某个完整组件? 2. 上面盖什么:标题、遮罩、按钮,还是状态层? 3. 覆盖层整体贴到哪里:顶部、中心、底部,还是某个角?

alignContent:先管整组子组件的默认位置

Stack({ alignContent: Alignment.BottomStart }) 里的 alignContent 是容器级的默认对齐方式。没有额外定位的子组件,会按这个默认值摆放。

在这个例子里,底部说明没有写 position

Column() {
  Text('ArkUI 布局入门')
  Text('Stack 适合底图、遮罩和底部信息浮层')
}
.width('100%')
.padding(12)
.backgroundColor('#00000080')

它会落在 Stack 的底部左侧,因为外层写了:

Stack({ alignContent: Alignment.BottomStart }) {
  // ...
}

规则记忆要点:如果覆盖层只有一个主要位置,优先让 StackalignContent 解决。

单个角标可以用 Stack,复杂控件留给 RelativeContainer

如果只是“图片右上角放一个 NEW”,Stack 也可以胜任。这个场景只有一个覆盖层,写法应该保持简单:

Stack({ alignContent: Alignment.TopEnd }) {
  Image($r('app.media.course_cover'))
    .width('100%')
    .height('100%')
    .objectFit(ImageFit.Cover)

  Text('NEW')
    .fontSize(11)
    .fontWeight(FontWeight.Bold)
    .fontColor(Color.White)
    .padding({ left: 8, right: 8, top: 3, bottom: 3 })
    .backgroundColor('#EF4444')
    .borderRadius(10)
    .margin({ top: 10, right: 10 })
}
.width('100%')
.height(180)

呈现的效果:

Pasted image 20260524224708

但如果需求变成这样:

  • 右上角有 NEW 角标。
  • 中间有播放按钮。
  • 底部有标题和说明。
  • 后面还可能加收藏、进度、标签、倒计时。

这时就不适合把所有控件都直接塞到 Stack 里各自找位置。更自然的结构是:

Stack:负责图片和整体覆盖区域
  Image:底图
  RelativeContainer:负责内部控件的相对定位
    Text:右上角角标
    Button:居中播放按钮
    Column:底部标题说明

RelativeContainer 后面单独讲。这里先把 Stack 的心智模型立住:它擅长做层叠,不擅长承担复杂控件之间的相对约束。

zIndex:顺序不清楚时显式写出来

Stack 的子组件会形成层叠关系。简单页面里,按代码顺序写通常够用:底图先写,覆盖层后写。

但业务 UI 变复杂后,可以给关键层写 zIndex

Image($r('app.media.course_cover'))
  .zIndex(0)

Column() {
  Text('ArkUI 布局入门')
}
.zIndex(1)

这样后面调整代码块顺序时,不容易把浮层压到图片下面。

Android XML:FrameLayout 是最接近的参照

传统 Android View 体系里,最接近 Stack 的容器是 FrameLayout。它也常用于“底图 + 底部浮层”:

<FrameLayout
    android:layout_width="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="180dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:orientation="vertical"
        android:padding="12dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ArkUI 布局入门" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stack 适合底图、遮罩和底部信息浮层" />
    </LinearLayout>
</FrameLayout>

对比 ArkUI,可以这样看:

  • FrameLayoutStack
  • android:scaleType="centerCrop".objectFit(ImageFit.Cover)
  • android:layout_gravity="bottom"Stack({ alignContent: Alignment.BottomStart })
  • 子 View 的覆盖关系 ↔ Stack 子组件顺序 / .zIndex(...)

如果读者没有 Android 背景,只记 ArkUI 这边也够:先放底层,再放覆盖层;覆盖层只有一个主要位置时,先用 alignContent

Compose:Box 的名字不同,思路相同

Jetpack Compose 里对应的是 Box

@Composable
fun CourseCoverCard() {
  Box(
    modifier = Modifier
      .fillMaxWidth()
      .height(180.dp)
      .clip(RoundedCornerShape(16.dp))
  ) {
    Image(
      painter = painterResource(R.drawable.course_cover),
      contentDescription = null,
      contentScale = ContentScale.Crop,
      modifier = Modifier.matchParentSize()
    )

    Column(
      modifier = Modifier
        .align(Alignment.BottomStart)
        .fillMaxWidth()
        .background(Color.Black.copy(alpha = 0.5f))
        .padding(12.dp)
    ) {
      Text("ArkUI 布局入门")
      Text("Stack 适合底图、遮罩和底部信息浮层")
    }
  }
}

Compose 的 Box 与 ArkUI 的 Stack 很像:容器负责叠放,覆盖层贴到底部、顶部或中心。差异主要在语法:Compose 把位置写进 Modifier,ArkUI 的这个例子用 StackalignContent 设默认位置。

什么时候用 Stack,什么时候不要用

适合用 Stack 的场景:

  • 多层内容共享同一块区域。
  • 需要底图、遮罩、底部信息浮层、简单角标。
  • 子组件之间主要是覆盖关系,而不是线性排列或相对约束关系。

不适合优先只用 Stack 的场景:

  • 组件只是横向或纵向排列,用 Row / Column 更直接。
  • 同一块封面里有多个控件要分别贴右上角、中心、底部、左右边缘,后面会讲 RelativeContainer
  • 页面级弹窗、半模态页面、Toast、全局浮层,优先看对应的弹窗/浮层能力,不要用普通 Stack 硬盖整个应用。

布局容器的选择可以按这个顺序判断:

横着排 / 竖着排 -> Row / Column
叠在同一块区域 -> Stack
需要换行和弹性分布 -> Flex
组件之间互相约束 -> RelativeContainer
多列和断点适配 -> GridRow / GridCol

到这里,线性布局和层叠布局就能覆盖很多基础页面:列表卡片、封面卡片、个人中心头图、视频卡片、商品卡片都可以先从这两个容器组合起来。等讲到 RelativeContainer,再把右上角角标、居中播放按钮、底部说明放回同一个复杂封面卡片里。

参考素材

  • HarmonyOS Developers:构建布局
  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-build-layout
  • HarmonyOS Developers:层叠布局(Stack)
  • https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-layout-development-stack-layout
  • Android Developers:FrameLayout
  • https://developer.android.com/reference/android/widget/FrameLayout
  • Android Developers:Compose layout basics(Box)
  • https://developer.android.com/develop/ui/compose/layouts/basics
  • HarmonyOS Samples:sample_in_harmonyos
  • https://gitcode.com/HarmonyOS_Samples/sample_in_harmonyos

// Kai@CodeHubble

// 观测坐标:Android-HarmonyOS/2026-05-20

上一篇
下一篇