一多开发:界面级自适应布局全解析

77 阅读7分钟

一多开发分为 界面级一多、功能级一多、工程级一多

一、界面级一多

自适应布局(微调)

1.拉伸能力(全部分配) .flexGrow() .flexShrink()

2.隐藏能力(显示优先级) .displayPriority()

3.均分能力(均匀分配) 主轴对齐

4.占比能力(宽高的比例) .layoutWeight() 百分百

5.缩放能力(宽高的比例,子组件宽高比不变) .aspectRatio

6.折行能力(自动换行) FlexWrap.Wrap

7.延伸能力(列表中的先后顺序) List组件 Scroll组件

响应式布局(大调)

1、断点

断点以应用窗口宽度为切入点,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,在不同的区间下,开发者可根据需要实现不同的页面布局效果。

默认四个断点

[0,320) xs 智能穿戴(手表等超小屏)

[320,600] sm 手机竖屏

[600,840] md 手机横屏、折叠屏

[840,+∞) lg 平板、PC(2in1设备)

如何获取窗口对象并监听窗口尺寸变化

1.在UIAbility的onWindowStageCreate生命周期回调中,通过窗口对象获取启动时的应用窗口宽度并注册回调函数监听窗口尺寸变化。

2.将窗口尺寸的长度单位由PX转换成vp后,根据屏幕宽度利用AppStorage共享当前断点。

3.监听屏幕变化,更新断点。

如何获取窗口对象并监听窗口尺寸变化?
import { window, display } from '@kit.ArkUI';
import { UIAbility } from '@kit.AbilityKit';
 
 
export default class MainAbility extends UIAbility {
  private windowObj?: window.Window;
  private curBp: string = '';
  //...
  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number) :void{
    /2.将窗口尺寸的长度单位由PX转换成vp后,根据屏幕宽度利用AppStorage共享当前断点。
    let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels;
    let newBp: string = '';
    if (windowWidthVp < 320) {
      newBp = 'xs';
    } else if (windowWidthVp < 600) {
      newBp = 'sm';
    } else if (windowWidthVp < 840) {
      newBp = 'md';
    } else {
      newBp = 'lg';
    }
    if (this.curBp !== newBp) {
      this.curBp = newBp;
      AppStorage.setOrCreate('currentBreakpoint', this.curBp);
    }
  }
 
 
  onWindowStageCreate(windowStage: window.WindowStage) :void{
    /1.通过窗口对象获取启动时的应用窗口宽度
    windowStage.getMainWindow().then((windowObj) => {
      this.windowObj = windowObj;
      // 获取应用启动时的窗口尺寸
      this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width);
      /3.注册回调函数,监听窗口尺寸变化
      windowObj.on('windowSizeChange', (windowSize)=>{
        this.updateBreakpoint(windowSize.width);
      })
    });
   // ...
  }
    
  //...
}
如何在UI中适配屏幕(小屏-红、中屏-绿,大屏-蓝)?

低级办法:三元

高级办法:映射

1.封装断点工具类,统一管理断点

2.将色值映射成对象{ 'sm':Color.Red, 'md':Color.Green, 'lg':Color.Blue }

3.实例化断点工具,将对象传入,根据运行时屏幕的尺寸自动匹配对应的断点值

export  class BreakPointType<T>{
  options:Record<string,T>
 
  constructor(options: Record<string, T>) {
    this.options = options;
  }
  
  getValue(currentBreakpoint:string){
    return this.options[currentBreakpoint]
  }
}
 
        .fontColor(
        /2.断点工具实例化,传入断点对应颜色对象
          new BreakPointType({
            'sm':Color.Red,
            'md':Color.Green,
            'lg':Color.Blue
          }).getValue(this.currentBreakpoint)
        )

2、媒体查询(mediaquery.matchMediaSync)

针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。 只需要查询断点值:【获取窗口对象并监听窗口尺寸】

查询屏幕特征【媒体查询】

媒体查询可以查询到很多屏幕的特征:

宽、高、深浅、圆屏、横屏、设备类型(TV、phone)、分辨率

如何使用媒体查询?

1.封装一个媒体查询类

2.设置断点的查询条件

3.基于设置好的查询条件,监听变化,判断当前断点,并使用AppStorage存储

4.移除监听器方法,避免性能浪费

5.将类实例化

6.组件中引入 AppStorage

7.在需要的位置使用 AppStorage 中保存的断点值查询结果

export const BREAKPOINT_KEY = 'currentBreakPoint'
 
 /1.封装工具类  媒体查询类
export class MediaQueryUtil {
  /2.设置查询条件
  xx = mediaquery.matchMediaSync('(0vp<=width<320vp)') 
  xx2 = mediaquery.matchMediaSync('(320vp<=width<600vp)') 
  xx3 = mediaquery.matchMediaSync('(600vp<=width<840vp)')
  xx4 = mediaquery.matchMediaSync('(840vp<=width)') 
 
  on() {
    /3.基于设置好的查询条件 监听变化
    this.xx.on('change', (res) => {
      // 只要查询条件变化 就会触发回调
      if (res.matches) {
        AppStorage.setOrCreate(BREAKPOINT_KEY, 'xs')
      }
    })
    this.xx2.on('change', (res) => {
      if (res.matches) {
        AppStorage.setOrCreate(BREAKPOINT_KEY, 'sm')
      }
    })
    this.xx3.on('change', (res) => {
      if (res.matches) {
        AppStorage.setOrCreate(BREAKPOINT_KEY, 'md')
      }
    })
    this.xx4.on('change', (res) => {
      if (res.matches) {
        AppStorage.setOrCreate(BREAKPOINT_KEY, 'lg')
      }
    })
  }
 
/4.移除监听器 避免性能浪费
  off() {
    this.xx.off('change')
    this.xx2.off('change')
    this.xx3.off('change')
    this.xx4.off('change')
  }
}
/5.实例化
export const mediaQueryUtil = new MediaQueryUtil()
  
/6.组件中引入 AppStorage
  @StorageProp(BREAKPOINT_KEY) curBP: string = '';
 
  //媒体查询
  aboutToAppear(): void {
    mediaQueryUtil.on()
  }
 
  aboutToDisappear(): void {
    mediaQueryUtil.off()
  }
/7.在需要的位置使用 AppStorage 中保存的断点值
        .fontColor(
          new BreakPointType({
            'xs': Color.Red,
            'sm': Color.Green,
            'md': Color.Blue,
            'lg': Color.Yellow
          }).getValue(this.curBP)
        )

3、栅格布局

将组件划分为有规律的多列,通过调整总列数,及子组件所占列数,实现不同布局

栅格容器GridRow a.columns 属性 设置屏幕多少等份 默认为12列

b.gutter 属性(排水渠) 相邻的两个Column之间的距离 默认为0

c.breakpoints 属性 自定义断点的取值范围同时启用更多断点,注意,修改的断点值后面必须加上vp单位。

子组件GridCol a.span 属性 设置子组件占的份数 默认1 12除法

b.offset 属性 设置子组件偏移的份数 默认0

c.onBreakpointChange 事件 断点发生变化时触发回调

columns、gutter、span、offset 都直接支持断点{ xs:1 ,sm:2 ,md:3 ,lg:4 }

@Entry
@Component
struct Demo10 {
  @State breakPoint: string = 'sm'
 
  // 颜色数组
  build() {
    Column() {
      // GridRow 默认支持 4 个断点
      //  xs:(0vp<=width<320vp) 智能穿戴,比如手表
      //  sm:(320vp<=width<600vp) 手机
      //  md:(600vp<=width<840vp) 折叠屏
      //  lg:(840vp<=width) 平板
      GridRow({
        // 4个断点 和默认的一样
        breakpoints: { value: ['320vp', '600vp', '840vp'] },
        gutter: 10, // 子组件间隙
        // columns: 12 // 统一设计列数 默认 12
        columns: {
          // 不同的断点分别设置不同的列数
          xs: 2, // 超小
          sm: 4, // 手机竖屏
          md: 8, // 折叠,手机横屏
          lg: 12 // 大屏
        }
      }) {
        ForEach(Array.from({ length: 2 }), (item: string, index: number) => {
          GridCol({
            // 每一行 2 个子元素,span 怎么设置(占的行数)
            // span: 2, // 占用列数 这样设置所有断点都是 2 列
            // 支持不同断点分别设置不同的占用列数
            span: {
              xs: 2,
              sm: 2,
              md: 2,
              lg: 4
            },
            // offset 偏移列数 默认为 0
            // offset: 1, // 偏移一列
            // 支持不同断点分别设置偏移不同的列数
            offset: {
              // xs: 2,
              // sm: 1
            }
          }) {
            Text(index.toString())
              .height(50)
          }
          .border({ width: 1 })
        })
      }
      .border({ width: 1, color: Color.Orange })
      .width('90%')
      .height('90%')
      // 断点发生变化时触发回调
      // breakpoint当前的断点 字符串
      .onBreakpointChange(breakpoint => {
        console.log('breakpoint:', breakpoint)
        this.breakPoint = breakpoint
      })
      Text(this.breakPoint)
        .width('100%')
        .textAlign(TextAlign.Center)
        .fontSize(30)
        .fontWeight(900)
    }
    .width('100%')
    .height('100%')
 
  }
}

响应式布局的使用场景

断点 获取屏幕宽度

媒体查询 查询屏幕特征

栅格布局 页面UI布局

二、功能级一多

系统能力几乎一致属于同范类,(手机、平板)(智能穿戴)(车机、智慧屏)

系统能力(即SystemCapability,缩写为SysCap)指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失。

如何适配系统能力?

方法1、使用canIUse接口判断设备是否支持某系统能力

方法2、通过import动态导入,配合try/catch

if (canIUse("能力集的名字")) {
  // 正常调用
} else {
  // 提示用户
   console.log("该设备不支持SystemCapability.Communication.NFC.Core")
}
if (canIUse("能力集的名字")) {
  // 正常调用
} else {
  // 提示用户
   console.log("该设备不支持SystemCapability.Communication.NFC.Core")
}

三、工程级一多

一多模式下,官方推荐在开发过程中采用"三层工程架构",其实就是把项目拆分成不同类型的模块,再通过模块之间的引用组合,最终实现应用功能,拆分规范如下:

commons(公共能力层):通用的工具类、资源 类似common文件夹 features(基础特性层):不同的业务模块 类似views文件夹 products(产品定制层):针对不同的设备定制应用 模块的分类 1、Ability 包类型:HAP 应用的功能模块,可以独立安装运行

2、Shared Library 包类型:HSP 动态共享包,运行时复用 不会多次拷贝

3、Static Library 包类型:HAR 静态共享包,编译时会复用,会多次拷贝,应用体积变大(适合用于三方库、二方库)