手绘笔记风信息图,主题是 HarmonyOS7 横向滚动分类栏实战拆解。画面中心是一条横向可滚动的餐

前言

提交订单、确认支付、保存资料这类页面经常需要吸底按钮。问题是按钮一吸底,最后一段内容、协议说明、价格明细很容易被挡住。我在 HarmonyOS7 里处理这类页面,会把滚动内容和底部操作区明确分开,并给列表底部留出真实空间。

吸底按钮的核心不是固定在底部,而是不遮挡用户必须阅读和确认的内容。

为什么这个问题经常被写乱

吸底按钮别挡住内容 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

布局拆分

区域 负责内容 注意点
顶部 标题、地址 不参与吸底
中间 商品、说明、协议 可滚动并留底部空隙
底部 总价、提交按钮 高度稳定

步骤上我会先写完整内容,再加底部栏,最后给滚动区域补 padding({ bottom }),不要靠猜测屏幕高度。

ArkUI/ArkTS 示例

class OrderLine {
  id: number = 0
  name: string = ''
  price: string = ''

  constructor(id: number, name: string, price: string) {
    this.id = id
    this.name = name
    this.price = price
  }
}

@Entry
@Component
struct BottomActionPage {
  @State agreed: boolean = false
  private goods: OrderLine[] = [
    new OrderLine(1, 'HarmonyOS7 开发手册', '¥69'),
    new OrderLine(2, 'ArkUI 实战课程', '¥199'),
    new OrderLine(3, '项目模板服务', '¥299')
  ]

  @Builder
  BottomBar() {
    Row({ space: 12 }) {
      Column({ space: 2 }) {
        Text('合计').fontSize(12).fontColor('#777777')
        Text('¥567').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#D9480F')
      }
      .alignItems(HorizontalAlign.Start)
      Blank()
      Button('提交订单')
        .enabled(this.agreed)
        .height(44)
        .onClick(() => {})
    }
    .padding(16)
    .backgroundColor('#FFFFFF')
    .shadow({ radius: 12, color: '#1A000000', offsetY: -2 })
  }

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Scroll() {
        Column({ space: 12 }) {
          Text('确认订单').fontSize(24).fontWeight(FontWeight.Bold)
          ForEach(this.goods, (item: OrderLine) => {
            Row() {
              Text(item.name).fontSize(16)
              Blank()
              Text(item.price).fontSize(16).fontWeight(FontWeight.Medium)
            }
            .padding(14)
            .backgroundColor('#FFFFFF')
            .borderRadius(10)
          }, (item: OrderLine) => item.id.toString())

          Row() {
            Checkbox().select(this.agreed).onChange((value: boolean) => { this.agreed = value })
            Text('我已阅读并同意服务协议,知道虚拟内容购买后不支持无理由退款。')
              .fontSize(13)
              .fontColor('#666666')
              .layoutWeight(1)
          }
          .padding(14)
          .backgroundColor('#FFFFFF')
          .borderRadius(10)
        }
        .padding({ left: 16, right: 16, top: 16, bottom: 104 })
      }
      .height('100%')
      this.BottomBar()
    }
    .backgroundColor('#F5F6FA')
  }
}

关键代码说明

Stack({ alignContent: Alignment.Bottom }) 让底部栏覆盖在页面下方,但滚动内容通过底部 padding 让出空间。

底部 padding 不是随便加的,它要大于底部栏高度,保证协议和最后一项能完整滚出来。

提交按钮禁用态 直接绑定 agreed,用户没勾选协议时不会误提交。

底部高度怎么估

内容 估算高度
上下 padding 32vp
按钮高度 44vp
阴影和安全余量 20 到 28vp

示例里底部 padding 用了 104,不是为了凑数,而是让协议区域滚到按钮上方后仍有呼吸空间。真实项目如果底部栏还包含优惠券、配送费、会员折扣,padding 也要跟着增加。

容易漏掉的场景

  • 横屏或折叠屏时底部栏仍然要有稳定高度。
  • 表单校验失败时,错误文案不能被底部栏挡住。
  • 页面有键盘输入时,吸底按钮和键盘避让要一起验证。
  • 底部按钮禁用时也要让用户知道原因,比如协议未勾选或库存不足。

吸底按钮要给内容让路

吸底按钮最容易出问题的地方,不是按钮本身,而是滚动内容的最后一屏。协议说明、退款规则、价格明细这些内容如果被按钮挡住,用户就只能靠猜,后面出了问题也很难说清楚。

示例里滚动区底部加了 104 的 padding,本质是在告诉布局:底部已经有一块固定操作区,内容需要提前让出空间。这个数值要根据底部栏真实高度来调,不要只在一种机型上看起来没问题就结束。

实际项目里我还会额外看三种场景:商品很多时最后一项能不能完整滚出来,协议文案变长后会不会被遮住,按钮禁用时用户能不能马上知道原因。吸底栏稳定,页面才不会在关键提交步骤掉链子。

小结

HarmonyOS7 吸底按钮要写得稳,重点是尊重内容阅读路径。按钮固定住了,内容也要能完整滚到用户眼前。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐