鸿蒙:使用断点和媒体查询实现响应式布局

1. 前言

断点,可以理解为划分不同屏幕尺寸的工具,也可以理解为将屏幕尺寸归为一个范围点。做断点的目的是,方便我们在不同屏幕尺寸的鸿蒙设备下实现响应式UI布局。何为响应式布局,我的理解是,屏幕尺寸变化,你的UI会发生变化,以实现最佳的视觉效果,这就是响应式布局。

2. 核心思路

  1. 用媒体查询接口做横竖屏切换功能。
  2. 根据官方文档推荐的vp尺寸,合理划分屏幕所处的断点,例如:'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'。
  3. UI依据断点的变化,布局发生变化。

3. 核心代码

复制代码
// 获取屏幕纵向断点str
getHeightBreakpointStr() {
  // HEIGHT_SM  0  窗口高宽比小于0.8。
  // HEIGHT_MD  1  窗口高宽比大于等于0.8,且小于1.2。
  // HEIGHT_LG  2  窗口高宽比大于等于1.2。

  const heightBreakpoint = this.getUIContext().getWindowHeightBreakpoint()
  console.info(` heightBreakpoint: ${heightBreakpoint}`);
  switch (heightBreakpoint) {
    case 0:
      this.heightBreakpoint = "sm"
      break;
    case 1:
      this.heightBreakpoint = "md"
      break;
    case 2:
      this.heightBreakpoint = "lg"
      break;
    default:
      break;
  }
  this.heightBreakpoint
}

// 获取屏幕横向断点str
getWidthBreakpointStr() {
  //  获取当前实例所在窗口的宽度断点枚举值
  // WIDTH_XS 0  窗口宽度小于320vp。
  // WIDTH_SM 1  窗口宽度大于等于320vp,且小于600vp。
  // WIDTH_MD 2  窗口宽度大于等于600vp,且小于840vp。
  // WIDTH_LG 3  窗口宽度大于等于840vp,且小于1440vp。
  // WIDTH_XL 4  窗口宽度大于等于1440vp。
  const widthBreakpoint = this.getUIContext().getWindowWidthBreakpoint()
  console.info(` widthBreakpoint: ${widthBreakpoint}`);
  switch (widthBreakpoint) {
    case 0:
      this.widthBreakpoint = "xs"
      break;
    case 1:
      this.widthBreakpoint = "sm"
      break;
    case 2:
      this.widthBreakpoint = "md"
      break;
    case 3:
      this.widthBreakpoint = "lg"
      break;
    case 4:
      this.widthBreakpoint = "xl"
      break;
    default:
      break;
  }

}

4. 运行效果

5. 完整代码

Index.ets

复制代码
import { mediaquery, window } from '@kit.ArkUI';

type BreakpointType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';

@Entry
@ComponentV2
struct Index {
  @Local currentState: string = ""
  listener: mediaquery.MediaQueryListener =
    this.getUIContext().getMediaQuery().matchMediaSync('(orientation: landscape)');
  // 获取当前应用的UIAbility上下文
  context = this.getUIContext().getHostContext()!;
  @Local widthBreakpoint: BreakpointType = "xs"
  @Local heightBreakpoint: BreakpointType = "xs"

  aboutToAppear(): void {
    // 监听屏幕方向改变
    this.getCurrentOrientation()


  }

  // 获取屏幕纵向断点str
  getHeightBreakpointStr() {
    // HEIGHT_SM  0  窗口高宽比小于0.8。
    // HEIGHT_MD  1  窗口高宽比大于等于0.8,且小于1.2。
    // HEIGHT_LG  2  窗口高宽比大于等于1.2。

    const heightBreakpoint = this.getUIContext().getWindowHeightBreakpoint()
    console.info(` heightBreakpoint: ${heightBreakpoint}`);
    switch (heightBreakpoint) {
      case 0:
        this.heightBreakpoint = "sm"
        break;
      case 1:
        this.heightBreakpoint = "md"
        break;
      case 2:
        this.heightBreakpoint = "lg"
        break;
      default:
        break;
    }
    this.heightBreakpoint
  }

  // 获取屏幕横向断点str
  getWidthBreakpointStr() {
    //  获取当前实例所在窗口的宽度断点枚举值
    // WIDTH_XS 0  窗口宽度小于320vp。
    // WIDTH_SM 1  窗口宽度大于等于320vp,且小于600vp。
    // WIDTH_MD 2  窗口宽度大于等于600vp,且小于840vp。
    // WIDTH_LG 3  窗口宽度大于等于840vp,且小于1440vp。
    // WIDTH_XL 4  窗口宽度大于等于1440vp。
    const widthBreakpoint = this.getUIContext().getWindowWidthBreakpoint()
    console.info(` widthBreakpoint: ${widthBreakpoint}`);
    switch (widthBreakpoint) {
      case 0:
        this.widthBreakpoint = "xs"
        break;
      case 1:
        this.widthBreakpoint = "sm"
        break;
      case 2:
        this.widthBreakpoint = "md"
        break;
      case 3:
        this.widthBreakpoint = "lg"
        break;
      case 4:
        this.widthBreakpoint = "xl"
        break;
      default:
        break;
    }

  }

  /// 获取当前方向状态
  getCurrentOrientation() {
    this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
      console.log("监听结果" + mediaQueryResult.media)
      this.getWidthBreakpointStr()
      this.getHeightBreakpointStr()
      if (mediaQueryResult.matches) {
        this.currentState = "横屏"
      } else {
        this.currentState = "竖屏"
      }
    });
  }

  /// 切换横屏
  switchToLandscape() {

    // 获取应用的最后一个窗口实例
    window.getLastWindow(this.context).then((lastWindow) => {
      // 根据参数设置窗口首选方向:横屏
      lastWindow.setPreferredOrientation(window.Orientation.LANDSCAPE)
    });

  }

  /// 切换竖屏
  switchToPortrait() {

    // 获取应用的最后一个窗口实例
    window.getLastWindow(this.context).then((lastWindow) => {
      // 根据参数设置窗口首选方向:竖屏
      lastWindow.setPreferredOrientation(window.Orientation.PORTRAIT)
    });

  }

  build() {


    Column({ space: this.currentState == "横屏" ? 20 : 50 }) {
      Text("响应式布局:断点+媒体查询")
        .fontSize(this.currentState == "横屏" ? 16 : 24)
        .fontWeight(FontWeight.Bold)

      Column({ space: 30 }) {
        Text("宽度断点" + this.widthBreakpoint)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)

        Text("纵向断点" + this.heightBreakpoint)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)

        Text("当前的状态:" + this.currentState)
          .fontSize(30)
          .fontWeight(FontWeight.Bold)
      }

      Column({ space: 20 }) {
        Button("点击切换横屏")
          .onClick(() => {
            this.switchToLandscape()
          })
          .backgroundColor(this.currentState == "横屏" ? Color.Black : Color.Blue)
        Button("点击切换竖屏")
          .onClick(() => {
            this.switchToPortrait()
          })
          .backgroundColor(this.currentState == "横屏" ? Color.Black : Color.Blue)
      }

      Row({ space: this.widthBreakpoint == "sm" ? 20 : 50 }) {
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)
        Image($r('app.media.startIcon'))
          .width(this.widthBreakpoint == "sm" ? 20 : 50)

      }
      .justifyContent(FlexAlign.Center)
      .width("100%")

    }
    .justifyContent(FlexAlign.Center)
    .width("100%")
    .height("100%")
  }
}

觉得有帮助,可以点赞或收藏

相关推荐
云端漫步19879 小时前
HarmonyOS 互动卡片实战:快递卡片与睡眠卡片完整开发流程
华为·harmonyos
智塑未来10 小时前
鸿蒙6.1隐私安心加倍:加密分享、星盾防诈、应用锁三重保护,守护到位
华为·harmonyos
byte轻骑兵10 小时前
2026手机远程办公:向日葵、TeamViewer、ToDesk鸿蒙适配+传文件+AI审计功能对比
智能手机·harmonyos·todesk·teamviewer·手机远程办公
贾伟康11 小时前
【知律|06】HarmonyOS ArkTS 法律分类实战:让民法、劳动、消费等入口可维护
harmonyos·arkts·arkui·分类设计·多设备
M-Robots echo1 天前
M-CLAW,面向具身智能的机器人任务编排开发框架
机器人·ros·鸿蒙·开源社区·m-robots
M-Robots echo1 天前
王成录:机器人产业下一个十年,拼的是 “系统级智能化” 而非单点算法堆叠
机器人·ros·鸿蒙·开源社区·m-robots·王成录
梦想不只是梦与想1 天前
鸿蒙 测试工具:DevEco Testing(一)
测试工具·harmonyos·testing
大锅盖11 天前
Web 工单要调用相机,第一步不是打开取景框,而是建立能力门禁
前端·数码相机·harmonyos
M-Robots echo1 天前
M-Robots ROS 兼容适配方案,实现存量机器人项目低成本迁移
机器人·ros·鸿蒙·开源社区·m-robots
贾伟康1 天前
【华夏二十四节气|07】HarmonyOS 6.0.2(22) ArkTS 节气搜索实战:多字段匹配与四态闭环
移动开发·harmonyos·arkts·arkui·本地搜索