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

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%")
  }
}

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

相关推荐
奋斗的小青年!!1 小时前
Flutter与OpenHarmony深度协同:SnackBar组件的跨平台适配实战
flutter·harmonyos·鸿蒙
行者961 小时前
OpenHarmony Flutter跨平台开发:树形视图组件的实践与性能优化
flutter·性能优化·harmonyos·鸿蒙
wszy180910 小时前
新文章标签:让用户一眼发现最新内容
java·python·harmonyos
wszy180910 小时前
顶部标题栏的设计与实现:让用户知道自己在哪
java·python·react native·harmonyos
Van_Moonlight10 小时前
RN for OpenHarmony 实战 TodoList 项目:空状态占位图
javascript·开源·harmonyos
anyup12 小时前
2026第一站:分享我在高德大赛现场学到的技术、产品与心得
前端·架构·harmonyos
anyup12 小时前
从赛场到产品:分享我在高德大赛现场学到的技术、产品与心得
前端·harmonyos·产品
Van_Moonlight14 小时前
RN for OpenHarmony 实战 TodoList 项目:顶部导航栏
javascript·开源·harmonyos
Swift社区14 小时前
H5 与 ArkTS 通信的完整设计模型
uni-app·harmonyos
程序猿追15 小时前
【鸿蒙PC桌面端实战】从零构建 ArkTS 高性能图像展示器:DevEco Studio 调试与 HDC 命令行验证全流程
华为·harmonyos