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

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

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

相关推荐
2601_9495936510 小时前
基础入门 React Native 鸿蒙跨平台开发:卡片组件
react native·react.js·harmonyos
沐芊屿10 小时前
华为交换机配置M-LAG
服务器·网络·华为
qq_1777673711 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_1777673711 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体
向哆哆12 小时前
构建跨端健身俱乐部管理系统:Flutter × OpenHarmony 的数据结构与设计解析
数据结构·flutter·鸿蒙·openharmony·开源鸿蒙
烬头882112 小时前
React Native鸿蒙跨平台采用了函数式组件的形式,通过 props 接收分类数据,使用 TouchableOpacity实现了点击交互效果
javascript·react native·react.js·ecmascript·交互·harmonyos
qq_1777673712 小时前
React Native鸿蒙跨平台通过Animated.Value.interpolate实现滚动距离到动画属性的映射
javascript·react native·react.js·harmonyos
向哆哆13 小时前
高校四六级报名管理系统的考试信息模块实现:Flutter × OpenHarmony 跨端开发实践
flutter·开源·鸿蒙·openharmony·开源鸿蒙
qq_1777673714 小时前
React Native鸿蒙跨平台实现消息列表用于存储所有消息数据,筛选状态用于控制消息筛选结果
javascript·react native·react.js·ecmascript·harmonyos
ujainu14 小时前
Flutter + OpenHarmony 实战:从零开发小游戏(三)——CustomPainter 实现拖尾与相机跟随
flutter·游戏·harmonyos