由于HarmonyOS設備的屏幕尺寸和分辨率各不相同,開發者需要采取適當的措施來適配不同的屏幕。
1.EntryAbility.ets文件里:onWindowStageCreate方法里判斷設備類型,
如果是pad,需全屏展示(按客戶需求來,本次需求按全屏展示),功能實現如下:
onWindowStageCreate(windowStage: window.WindowStage): void {windowStage.getMainWindow().then((windowClass) => {try {//判斷是否是padif(deviceInfo.deviceType == 'tablet') {//pad//pad橫屏let orientation = window.Orientation.AUTO_ROTATION_LANDSCAPE_RESTRICTED;let promise = windowClass.setPreferredOrientation(orientation);//如果是pad橫屏promise.then(() => {console.info('Succeeded in setting the window orientation.');}).catch((err: BusinessError) => {console.error(`Failed to set the window orientation. Cause code: ${err.code}, message: ${err.message}`);});}}catch (exception) {console.error(`Failed to set window orientation. Cause code: ${exception.code}, message: ${exception.message}`);}// 獲取窗口尺寸,存入AppStorageAppStorage.setOrCreate('winWidth', windowClass.getWindowProperties().windowRect.width);AppStorage.setOrCreate('winHeight', windowClass.getWindowProperties().windowRect.height);// 監聽窗口尺寸變化windowClass.on('windowSizeChange', (windowSize) => {//監聽窗口尺寸變化AppStorage.setOrCreate('winWidth', windowSize.width);//如果窗口變化了,立馬更新存儲值-寬AppStorage.setOrCreate('winHeight', windowSize.height);//如果窗口變化了,立馬更新存儲值-高console.log('寬高',String(windowSize.width), String(windowSize.height))//});});// Main window is created, set main page for this abilityconsole.info('onWindowStageCreate');AppStorage.setOrCreate('windowStage',windowStage);AppUtil.init(this.context);try {let windowClass: window.Window = windowStage.getMainWindowSync()AppStorage.setOrCreate('windowClass', windowClass)// 設置窗口全屏windowClass.setWindowLayoutFullScreen(true)let topRect = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT).topRect//獲取頂部安全區域let bottomRect = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect//獲取底部安全區域// 緩存window窗口對象AppStorage.setOrCreate('windowClass', windowClass);AppStorage.setOrCreate('bottomAreaRectHeight', bottomRect.height);//把height-底部安全區域存儲AppStorage.setOrCreate('topAreaRectHeight', topRect.height );//height-頂部安全區域存儲this.updateBreakPoint(windowClass.getWindowProperties().windowRect.width, windowClass.getWindowProperties().windowRect.height);//獲取窗口的寬 度和高度,計算此時是什么場景:直板機、折疊屏、pad等windowClass.on('windowSizeChange', (windowSize: window.Size) => {console.log('windowSizeChange',windowSize.width, windowSize.height)this.updateBreakPoint(windowSize.width, windowSize.height);//})} catch (exception) {Logger.error(`Failed to obtain the main window. Cause code: ${exception.code}, message: ${exception.message}`);}windowStage.loadContent('pages/LaunchPage', (err, data) => { if (err.code) { Logger.error('Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } }); }private updateBreakPoint(windowWidth: number, windowHeight: number): void {let curBp: string = '';let type = deviceInfo.deviceTypeif (type=='2in1'){//如果是電腦,直接走pad的適配(需要注意,如果是電腦,需要修改一些交互,比如鍵盤(onKeyEvent事件))AppStorage.setOrCreate('breakPoint', 'xl')return}// 平板,下面的就是根據當前實時屏幕尺寸,計算是處于哪種情況 md-直板機 lg-折疊屏 xl-pad(前面已提到,電腦的UI和pad的UI一致)if(type == 'tablet') {curBp = 'xl'AppStorage.setOrCreate('breakPoint', curBp)} else {console.log('windowHeight/windowWidth',windowWidth, windowHeight, windowHeight/windowWidth > 1.5)let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels;if (windowWidthVp < 700) {curBp = 'md'} else {curBp = 'lg'}if(windowHeight/windowWidth > 1.5 == true) {curBp = 'md'} else {if(windowHeight<1500) {curBp = 'md'} else {curBp = 'lg'}}let ret: boolean = false;ret = display.isFoldable();console.log("curBp", curBp, windowWidthVp)if(deviceInfo.deviceType == 'phone' && ret == false) {AppStorage.setOrCreate('breakPoint', 'md')} else {AppStorage.setOrCreate('breakPoint', curBp)}}console.log('設備類型', curBp)}//以上的內容都是在應用入口完成,判斷好設備類型和視口大小
2.上面是具體說明了判斷設備的類型,以及監聽窗口大小變化來更改視口類型,下面的內容主要是來介紹如何根據設備類型來適配UI:
其實在鴻蒙里,vp和安卓的dp單位等比,開發過程中,可以直接調成安卓單位的UI來開發,就可以兼容所有的設備(字體和組件寬高比,會自適應),我們需要關注的是,在不同設備下,布局的改變,這個時候需要根據我們計算出來的 breakPoint(xl:電腦,pad , lg:折疊屏 , md:手機)
export class BreakpointConstants{ //BreakpointConstants.ets工具類,md(直板機) lg(折疊屏) xl(電腦,平板)/*** Breakpoints that represent small device types.*/static readonly BREAKPOINT_SM: string = 'sm';/*** Breakpoints that represent middle device types.*/static readonly BREAKPOINT_MD: string = 'md';/*** Breakpoints that represent large device types.*/static readonly BREAKPOINT_LG: string = 'lg';/*** Breakpoints that represent large device types.*/static readonly BREAKPOINT_XL: string = 'xl';/*** Current breakpoints that to query the device types.*/static readonly CURRENT_BREAKPOINT: string = 'currentBreakpoint';/*** Range of the small device width.*/static readonly RANGE_SM: string = '(320vp<=width<520vp)';/*** Range of the middle device width.*/static readonly RANGE_MD: string = '(520vp<=width<840vp)';/*** Range of the large device width.*/static readonly RANGE_LG: string = '(840vp<=width)';/*** Range of the largePlus device width.*/static readonly RANGE_XL: string = '(1700vp<=width<=2720vp)';
}
使用方法如下:
//聲明一個變量,記錄當前視口大小(md,lg,xl)(EntryAbility.ets存儲值)@StorageLink('breakPoint') breakPoint: string = BreakpointConstants.BREAKPOINT_MD
布局兼容如下:
if (this.breakPoint=='xl') { // pad 和 PC}else{ // 手機和雙折疊}
雙折疊適配如下:
①使用Flex布局
Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center, wrap: FlexWrap.Wrap }) {Column(){}.width(this.breakPoint == BreakpointConstants.BREAKPOINT_MD ? '100%' : '49%')Column(){}.width(this.breakPoint == BreakpointConstants.BREAKPOINT_MD ? '100%' : '49%')
}
②使用GridRow布局
GridRow() {GridCol({ span: { sm: 12, md: 6, lg: 4 } }) {Column(){}}GridCol({ span: { sm: 12, md: 6, lg: 8 } }) {Column(){}}}.width('100%').height('100%')