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

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

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

相关推荐
电化学仪器白超18 小时前
④使用 PPTSYNC.exe 与华为手机拍照制作 GIF 动画
服务器·华为·智能手机
CN-cheng18 小时前
Flutter项目在HarmonyOS(鸿蒙)运行报错问题总结
flutter·华为·harmonyos·flutter运行到鸿蒙
碧波bibo19 小时前
在编译OpenHarmony遇到third_party/libnl编译报错的修复办法
鸿蒙
平平不平凡19 小时前
鸿蒙音频播放器深度解析:从核心设计到完整实现
harmonyos
HMSCore1 天前
【FAQ】HarmonyOS SDK 闭源开放能力 — Form Kit
harmonyos
小Mei数码说1 天前
华为WATCH D2 & WATCH GT6:秋季来临,尊爱老人的智能守护
华为
极客范儿1 天前
华为HCIP网络工程师认证—网络参考模型
网络·华为
摘星编程1 天前
昇腾NPU性能调优实战:INT8+批处理优化Mistral-7B全记录
人工智能·华为·gitcode·昇腾
爱笑的眼睛111 天前
HarmonyOS数据存储Kit深度实践:从架构设计到性能优化
华为·harmonyos
爱笑的眼睛111 天前
HarmonyOS后台代理提醒Agent:构建智能提醒功能的深度解析
华为·harmonyos