3、百分比与权重布局:使用百分比单位(%)和layoutWeight属性,实现父容器内的空间分配。

说到布局,很多新手会问:「我到底该用dp还是百分比?」

我的答案是——两者都要用,但用对地方

百分比和权重,说白了就是让子组件「按比例分地盘」。你想想看,如果每个组件都写死宽度,换个屏幕就崩了。但用百分比或权重,父容器多大,子组件就跟着变。这才是自适应布局的精髓。

3.1 百分比单位(%):按父容器比例分配

鸿蒙的百分比单位,是相对于直接父容器的尺寸来计算的。

比如父容器宽度是300vp,你写width: "50%",那子组件就是150vp。父容器变到600vp,子组件自动变成300vp。嗯,就是这么简单。

核心要点:

  • 百分比只对直接父容器生效,别指望它穿透到祖父级
  • 支持 width、height、margin、padding 等属性
  • 父容器必须有明确的尺寸,否则百分比会失效

我在项目中遇到过一个问题:给一个组件设了height: "50%",结果死活不生效。排查了半天才发现,它的父容器没设高度,用的是wrapContent。父容器自己都没高度,50%自然就无从谈起了。

来看个实际例子:

@Entry
@Component
struct PercentDemo {
  build() {
    Column() {
      // 父容器占满屏幕宽度
      Column() {
        Text("我是50%宽度")
          .width("50%")
          .height(40)
          .backgroundColor("#FFB6C1")
          .textAlign(TextAlign.Center)
        
        Text("我是80%宽度")
          .width("80%")
          .height(40)
          .backgroundColor("#87CEEB")
          .textAlign(TextAlign.Center)
        
        Text("我是100%宽度")
          .width("100%")
          .height(40)
          .backgroundColor("#98FB98")
          .textAlign(TextAlign.Center)
      }
      .width("100%")
      .height("auto")
      .padding(10)
      .backgroundColor("#F0F0F0")
    }
    .width("100%")
    .height("100%")
  }
}

这段代码的效果很直观:三个子组件分别占父容器的50%、80%、100%。父容器宽度是屏幕宽度,所以它们会跟着屏幕一起缩放。

我的小技巧:

百分比适合用在「相对比例固定」的场景。比如左侧菜单占30%,右侧内容占70%。这种比例关系不会因为屏幕变化而改变,用百分比最合适。

3.2 layoutWeight:更优雅的「按权重分地盘」

百分比有个缺点——你得自己算。比如三个组件要按1:2:3分配,用百分比你得算成16.67%、33.33%、50%。多麻烦啊。

这时候layoutWeight就派上用场了。你只需要告诉它「权重值」,系统自动帮你算比例。

layoutWeight 的规则:

  • 所有设置了layoutWeight的组件,按权重值分配剩余空间
  • 没设layoutWeight的组件,先占自己的固定尺寸,剩下的再分
  • 权重值可以是整数或小数,比如1、2、3或者0.5、1.5

我记得有一次做平板适配,左侧导航栏固定80vp,右侧内容区要占满剩余空间。用百分比算半天都不对,换成layoutWeight一行代码搞定。

看代码:

@Entry
@Component
struct WeightDemo {
  build() {
    Row() {
      // 左侧固定宽度
      Column() {
        Text("导航栏")
          .width("100%")
          .height("100%")
          .backgroundColor("#FFD700")
          .textAlign(TextAlign.Center)
      }
      .width(80)  // 固定80vp
      .height("100%")
      
      // 右侧按权重分配
      Column() {
        Text("内容区 - 权重2")
          .width("100%")
          .height("100%")
          .backgroundColor("#87CEEB")
          .textAlign(TextAlign.Center)
      }
      .layoutWeight(2)
      .height("100%")
      
      Column() {
        Text("侧边栏 - 权重1")
          .width("100%")
          .height("100%")
          .backgroundColor("#98FB98")
          .textAlign(TextAlign.Center)
      }
      .layoutWeight(1)
      .height("100%")
    }
    .width("100%")
    .height("100%")
  }
}

这个例子中,左侧导航栏固定80vp,剩下的空间被右侧两个组件按2:1瓜分。内容区占2份,侧边栏占1份。屏幕越大,它们跟着变大,比例始终不变。

注意:

layoutWeight 只在 Row 或 Column 这种线性布局中生效。如果你用 Stack 或 Flex,它是不认的。我刚开始学的时候就在这上面栽过跟头,排查了半天才发现是布局容器用错了。

3.3 百分比 vs layoutWeight:怎么选?

说实话,这两个东西各有各的适用场景。我个人的习惯是这样的:

场景 推荐方案 原因
比例固定,且所有子组件都按比例分 layoutWeight 不用算,代码更简洁
部分固定、部分按比例 layoutWeight 固定部分写死,剩余空间自动分配
单个组件的宽高比例 百分比 比如宽度占父容器60%,高度自适应
嵌套布局中的比例 百分比 权重只能管一层,百分比可以层层传递
需要精确控制边距 百分比 margin和padding也支持百分比

你想想看,如果整个页面都是按比例分的,用layoutWeight多省事。但如果只是某个组件要占一半,用百分比更直接。

3.4 避坑指南:我踩过的那些坑

做自适应布局,坑是真的多。我挑几个典型的说说:

坑1:父容器没设尺寸,百分比失效

我曾经写了一个页面,给子组件设了width: "50%",结果怎么调都不对。后来发现父容器用的是wrapContent,它自己都没宽度,50%自然就是0了。记住:百分比依赖父容器的明确尺寸

坑2:权重和固定尺寸混用,顺序很重要

在Row或Column中,先渲染固定尺寸的组件,再渲染有权重的组件。如果你把固定尺寸的组件写在后面,它可能会被挤到一边去。我建议:固定尺寸的组件放前面,有权重的放后面

坑3:百分比和权重不能同时用在一个组件上

如果你给一个组件同时设了width: "50%"和layoutWeight(1),系统会优先用layoutWeight,百分比被忽略。别问我怎么知道的...嗯,调试了半天才发现。

3.5 实战:做一个自适应卡片布局

光说不练假把式。我们来做一个实际点的例子:一个卡片列表,每行显示3个卡片,卡片宽度按比例分配。

@Entry
@Component
struct CardLayout {
  build() {
    Column() {
      // 第一行:3个卡片,按1:1:1分配
      Row() {
        CardItem("卡片A", "#FFB6C1")
          .layoutWeight(1)
        CardItem("卡片B", "#87CEEB")
          .layoutWeight(1)
        CardItem("卡片C", "#98FB98")
          .layoutWeight(1)
      }
      .width("100%")
      .height(120)
      .padding(10)
      
      // 第二行:中间卡片占2份,两边各1份
      Row() {
        CardItem("左", "#FFD700")
          .layoutWeight(1)
        CardItem("中(重点)", "#FF6347")
          .layoutWeight(2)
        CardItem("右", "#FFD700")
          .layoutWeight(1)
      }
      .width("100%")
      .height(120)
      .padding(10)
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#F5F5F5")
  }
}

@Component
struct CardItem {
  private title: string = ""
  private bgColor: string = "#FFFFFF"
  
  build() {
    Column() {
      Text(this.title)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)
    }
    .width("100%")
    .height("100%")
    .backgroundColor(this.bgColor)
    .borderRadius(8)
    .margin(5)
    .shadow({ radius: 4, color: "#33000000" })
  }
}

这个例子中,第一行三个卡片平分宽度,第二行中间卡片占2份、两边各1份。不管屏幕多大,比例始终不变。这就是自适应布局的魅力——一次开发,多屏适配

进阶技巧:

如果你想让卡片在横屏和竖屏下有不同的排列方式,可以结合媒体查询(MediaQuery)来动态调整权重值。比如竖屏时每行2个卡片(权重1:1),横屏时每行3个(权重1:1:1)。这个我们后面章节会详细讲。

好了,百分比和权重布局就讲到这里。说白了,这两个工具就是让你「按比例分地盘」的。记住:固定尺寸用dp,比例分配用百分比或权重。下节课我们聊聊更灵活的Flex布局,到时候你会发现,权重和Flex结合起来用,效果更炸裂。