【鸿蒙 HarmonyOS 4.0】多设备响应式布局

时间:2024-03-06 17:17:33

一、背景

在渲染页面时,需要根据不同屏幕大小渲染出不同的效果,动态的判断设备屏幕大小,便需要采用多设备响应式布局。这种设计方法能够动态适配各种屏幕大小,确保网站在不同设备上都能呈现出最佳的效果。

二、媒体查询(mediaquery)

媒体查询作为响应式设计的核心,在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。媒体查询常用于下面两种场景:

  1. 针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
  2. 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。

2.1、引入与使用流程

设备状态

媒体查询条件

SM (320vp<=width<600vp)
MD (600vp<=width<840vp)
LG (840vp<=width)

①导入媒体查询模块

import mediaquery from '@ohos.mediaquery';

②设置媒体查询条件,并获取对应的listener

let listener = mediaquery.matchMediaSync('(320vp<=width<600vp)');

③给listener设置回调函数,当设备状态变化时会执行回调函数

//设置监听回调函数
listener.on('change',result => {
  //判断是否满足媒体查询条件
  if(result.matches){
    //记录当前设备状态
  }
})

④将设备状态记录到全局状态中

AppStorage.SetOrCreate('currentBreakpoint','SM')
@StorageProp('currentBreakpoint') currentBreakpoint:string = 'SM'

2.2、具体实现

2.2.1、案例说明

⭐背景情况:在渲染页面时,不同屏幕大小渲染出效果是一样的,tabs都是在底部

⭐期望:根据不同屏幕大小渲染出不同的效果,tabs动态展示

2.2.2、实现步骤

定义不同大小屏幕设备的Breakpoints 标记文件

import BreakpointType from '../bean/BreanpointType';
export default class BreakpointConstants {
  /**
   * 小屏幕设备的 Breakpoints 标记.
   */
  static readonly BREAKPOINT_SM: string = 'sm';

  /**
   * 中等屏幕设备的 Breakpoints 标记.
   */
  static readonly BREAKPOINT_MD: string = 'md';

  /**
   * 大屏幕设备的 Breakpoints 标记.
   */
  static readonly BREAKPOINT_LG: string = 'lg';

  /**
   * 当前设备的 breakpoints 存储key
   */
  static readonly CURRENT_BREAKPOINT: string = 'currentBreakpoint';

  /**
   * 小屏幕设备宽度范围.
   */
  static readonly RANGE_SM: string = '(320vp<=width<600vp)';

  /**
   * 中屏幕设备宽度范围.
   */
  static readonly RANGE_MD: string = '(600vp<=width<840vp)';

  /**
   * 大屏幕设备宽度范围.
   */
  static readonly RANGE_LG: string = '(840vp<=width)';

  static readonly BAR_POSITION: BreakpointType<BarPosition> = new BreakpointType({
    sm: BarPosition.End,
    md: BarPosition.Start,
    lg: BarPosition.Start,
  })
}

定义媒体查询工具类,用来完成监听器的自定义和使用,并将监听器进行存储

import mediaQuery from '@ohos.mediaquery';
import BreakpointConstants from '../constants/BreakpointConstants'

export default class BreakpointSystem{
  //定义3个屏幕监听器
  private smListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_SM)
  private mdListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_MD)
  private lgListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(BreakpointConstants.RANGE_LG)

  //定义回调函数
  smListenerCallback(result: mediaQuery.MediaQueryResult){
    if(result.matches){
      this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_SM)
    }
  }

  mdListenerCallback(result: mediaQuery.MediaQueryResult){
    if(result.matches){
      this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_MD)
    }
  }

  lgListenerCallback(result: mediaQuery.MediaQueryResult){
    if(result.matches){
      this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_LG)
    }
  }
  //定义存储函数,以上3个函数中调用
  updateCurrentBreakpoint(breakpoint: string){
    AppStorage.SetOrCreate(BreakpointConstants.CURRENT_BREAKPOINT, breakpoint)
  }
  //监听
  register(){
    this.smListener.on('change', this.smListenerCallback.bind(this))
    this.mdListener.on('change', this.mdListenerCallback.bind(this))
    this.lgListener.on('change', this.lgListenerCallback.bind(this))
  }
  //取消监听
  unregister(){
    this.smListener.off('change', this.smListenerCallback.bind(this))
    this.mdListener.off('change', this.mdListenerCallback.bind(this))
    this.lgListener.off('change', this.lgListenerCallback.bind(this))
  }
}

/*
 * 1、定义3个屏幕监听器
 * 2、分别监听不同的屏幕获取宽度范围
 * (先定义3个屏幕的函数,定义函数时使用AppStorage进行存储)+(再进行绑定,利用on监听,off取消监听)
 * */

③定义标记类型,页面中使用

//定义标记类型
declare interface BreakpointTypeOptions<T>{
  sm?:T,
  md?:T,
  lg?:T
}

export default class BreakpointType<T>{
  options: BreakpointTypeOptions<T>

  constructor(options: BreakpointTypeOptions<T>) {
    this.options = options
  }

  getValue(breakpoint: string): T{
    return this.options[breakpoint]
  }
}

④在页面中使用,不同屏幕展示不同大小

说明:
在首页中使用breakpointSystem完成初始化

在当前组件构建之前,去调用,完成注册;在组件销毁时,完成取消注册

tabs根据不同屏幕大小来显示方向位置

2.2.3、最终效果