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

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

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

相关推荐
祭曦念29 分钟前
【共创季稿事节】鸿蒙原生ArkTS动态列表布局实战_State_ForEach完整指南
华为·harmonyos
不羁的木木40 分钟前
《HarmonyOS 6.1 新能力实战之智感握姿》第二篇:核心功能——查询与监听握持手状态
华为·harmonyos
风华圆舞1 小时前
鸿蒙 + Flutter 下 AI 页面的状态协同设计
人工智能·flutter·harmonyos
互联网散修1 小时前
鸿蒙实战:仿小红书“我”的页面——从分层架构到沉浸式交互
交互·harmonyos
aqi002 小时前
一文速览 HarmonyOS 6.1.1 推出的十个新特性
android·华为·harmonyos·鸿蒙·harmony
木咺吟3 小时前
鸿蒙原生应用实战(三):游戏详情页与交互功能 — 400行ArkTS深度实践
harmonyos
芒鸽3 小时前
HarmonyOS 数据持久化开发实战:KVStore、关系型数据库与 Preferences
数据库·华为·harmonyos
TrisighT3 小时前
ArkTS 组件传对象还是拆属性?我测了帧率,结果和直觉反着来
harmonyos·arkts
木咺吟3 小时前
鸿蒙原生应用开发实战(四):电影详情与评分评价 — 电影清单App
harmonyos
风华圆舞3 小时前
鸿蒙语音播报功能 的 Flutter 侧封装思路
flutter·华为·harmonyos