/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mediaquery from '@ohos.mediaquery';
import { DeviceUtil } from '../utils/BaseUtils';
import { LogUtil } from '../utils/LogUtil';
import { UIExtensionSessionUtils } from '../utils/UIExtensionSessionUtils';
const TAG: string = 'ColumnSystemManager : ';
const COLUMN_GUTTER_SM = 16;
const COLUMN_GUTTER_MD = 16;
const COLUMN_GUTTER_LG = 24;
const COLUMN_MARGIN_SM = 16;
const COLUMN_MARGIN_MD = 16;
const COLUMN_MARGIN_LG = 24;
const DOUBLE = 2;
const DEFAULT_BTN_WIDTH = 188;
/**
* OOBE栅格系统管理类
*/
export class ColumnSystemManager {
private static instance: ColumnSystemManager;
private hasRegister: boolean = false;
private lgListener?: mediaquery.MediaQueryListener;
private mdListener?: mediaquery.MediaQueryListener;
private smListener?: mediaquery.MediaQueryListener;
private columnCount: number = 0;
private singleColumnWidth: number = 0;
private columnGutter: number = vp2px(COLUMN_GUTTER_SM);
private columnMargin: number = vp2px(COLUMN_MARGIN_MD);
constructor() {
this.initListeners();
}
/**
* 获取实例
*
* @returns 栅格管理类
*/
static getInstance(): ColumnSystemManager {
if (!ColumnSystemManager.instance) {
ColumnSystemManager.instance = new ColumnSystemManager();
}
return ColumnSystemManager.instance;
}
/**
* 注册媒体监听
*/
registerMediaQuery(): void {
if (this.hasRegister) {
return;
}
this.lgListener?.on('change', this.onColumnLg);
this.mdListener?.on('change', this.onColumnMd);
this.smListener?.on('change', this.onColumnSm);
this.hasRegister = true;
}
/**
* 反注册媒体监听
*/
unregisterMediaQuery(): void {
if (this.lgListener) {
this.lgListener.off('change');
}
if (this.mdListener) {
this.mdListener.off('change');
}
if (this.smListener) {
this.smListener.off('change');
}
this.hasRegister = false;
}
/**
* 获取当前窗口栅格总数
*
* @returns 栅格总数
*/
getColumnCount(): number {
return this.columnCount;
}
/**
* 获取单个栅格宽度,单位px
*
* @returns 单个栅格宽度
*/
getSingleColumnWidth(): number {
return this.singleColumnWidth;
}
/**
* 获取单个栅格宽度,单位vp
*
* @returns 单个栅格宽度
*/
getSingleColumnWidthVp(): number {
return px2vp(this.singleColumnWidth);
}
/**
* 获取栅格屏幕左右间距,单位px
*
* @returns 屏幕左右间距
*/
getColumnMargin(): number {
return this.columnMargin;
}
/**
* 获取栅格屏幕左右间距,单位vp
*
* @returns 屏幕左右间距
*/
getColumnMarginVp(): number {
return px2vp(this.columnMargin);
}
/**
* 获取栅格与栅格之间的间距,单位px
*
* @returns 栅格间距
*/
getColumnGutter(): number {
return this.columnGutter;
}
/**
* 获取栅格与栅格之间的间距,单位vp
*
* @returns 栅格间距
*/
getColumnGutterVp(): number {
return px2vp(this.columnGutter);
}
/**
* 获取指定栅格数量的宽度,包含栅格间距,即2个栅格包含1个gutter,单位px
*
* @param wantCount 指定栅格数量
* @param extraGutterCount 额外gutter数量,即在最外边添加多少个gutter
* @returns 指定栅格数量的宽度
*/
getCustomWidth(wantCount: number, extraGutterCount: number = 0): number {
if (wantCount <= 0) {
return 0;
}
return wantCount * this.singleColumnWidth + (wantCount - 1 + extraGutterCount) * this.columnGutter;
}
/**
* 获取指定栅格数量的宽度,包含栅格间距,即2个栅格包含1个gutter,单位vp
*
* @param wantCount 指定栅格数量
* @param extraGutterCount 额外gutter数量,即在最外边添加多少个gutter
* @returns 指定栅格数量的宽度
*/
getCustomWidthVp(wantCount: number, extraGutterCount: number = 0): number {
return px2vp(this.getCustomWidth(wantCount, extraGutterCount));
}
private initListeners(): void {
this.lgListener = mediaquery.matchMediaSync(`(${ColumnDevice.LG}vp<width)`);
this.mdListener = mediaquery.matchMediaSync(`(${ColumnDevice.MD}vp<=width<=${ColumnDevice.LG}vp)`);
this.smListener = mediaquery.matchMediaSync(`(${ColumnDevice.SM}vp<=width<${ColumnDevice.MD}vp)`);
}
private onColumnLg = (result: mediaquery.MediaQueryResult) => {
if (result.matches) {
this.onColumnChangeInner(ColumnCount.LG_COLUMN_COUNT);
}
};
private onColumnMd = (result: mediaquery.MediaQueryResult) => {
if (result.matches) {
this.onColumnChangeInner(ColumnCount.MD_COLUMN_COUNT);
}
};
private onColumnSm = (result: mediaquery.MediaQueryResult) => {
if (result.matches) {
this.onColumnChangeInner(ColumnCount.SM_COLUMN_COUNT);
}
};
private async onColumnChangeInner(count: number): Promise<void> {
this.columnCount = count;
this.updateMarginAndGutter();
this.calcColumnWidth();
AppStorage.setOrCreate('columnCount', count);
}
/* instrument ignore next */
private updateButtonWidth(): void {
let footBtnWidth: number = DEFAULT_BTN_WIDTH;
if (this.columnCount === ColumnCount.SM_COLUMN_COUNT) {
footBtnWidth = ColumnSystemManager.getInstance().getCustomWidthVp(2, 2);
} else if (DeviceUtil.isDevicePc()) {
footBtnWidth = ColumnSystemManager.getInstance().getCustomWidthVp(2, 0);
} else {
footBtnWidth = ColumnSystemManager.getInstance().getCustomWidthVp(3, 0);
}
LogUtil.showInfo(TAG, `updateButtonWidth: ${footBtnWidth}`);
AppStorage.setOrCreate('oobeFootBtnWidth', footBtnWidth);
}
/**
* 自动获取窗口大小并计算单个栅格宽度
*
* @returns 空Promise对象
*/
async calcColumnWidth(): Promise<void> {
let windowRect = await UIExtensionSessionUtils.getWindowRect();
if (!windowRect) {
LogUtil.showInfo(TAG, `get windowRect failed: calcColumnWidth oobeFootBtnWidth ${DEFAULT_BTN_WIDTH}`);
AppStorage.setOrCreate('oobeFootBtnWidth', DEFAULT_BTN_WIDTH);
return;
}
let windowWidth = windowRect.width;
this.singleColumnWidth = (windowWidth - this.columnMargin * DOUBLE - (this.columnCount - 1) * this.columnGutter) /
this.columnCount;
this.updateButtonWidth();
}
/* instrument ignore next */
private updateMarginAndGutter(): void {
if (this.columnCount === ColumnCount.SM_COLUMN_COUNT) {
this.columnMargin = vp2px(COLUMN_MARGIN_SM);
this.columnGutter = vp2px(COLUMN_GUTTER_SM);
} else if (this.columnCount === ColumnCount.MD_COLUMN_COUNT) {
this.columnMargin = vp2px(COLUMN_MARGIN_MD);
this.columnGutter = vp2px(COLUMN_GUTTER_MD);
} else {
this.columnMargin = vp2px(COLUMN_MARGIN_LG);
this.columnGutter = vp2px(COLUMN_GUTTER_LG);
}
}
/**
* 通过屏幕宽度获取当前的栅格数量
*
* @param currentWidthVp 当前屏幕宽度
* @returns 当前栅格数量
*/
static getCurrentColumnCountByWidth(currentWidthVp: number): number {
let currentColumnCount = ColumnCount.LG_COLUMN_COUNT as number;
if (currentWidthVp >= ColumnDevice.MD && currentWidthVp <= ColumnDevice.LG) {
currentColumnCount = ColumnCount.MD_COLUMN_COUNT;
} else if (currentWidthVp < ColumnDevice.MD) {
currentColumnCount = ColumnCount.SM_COLUMN_COUNT;
} else {
currentColumnCount = ColumnCount.LG_COLUMN_COUNT;
}
return currentColumnCount;
}
/**
* 通过当前栅格数量获取栅格间隙Gutter
*
* @param currentColumnCount 当前栅格数量
* @returns 当前栅格间隙
*/
static getColumnGutterVpByColumnCount(currentColumnCount: number) {
if (currentColumnCount === ColumnCount.SM_COLUMN_COUNT) {
return COLUMN_GUTTER_SM;
} else if (currentColumnCount === ColumnCount.MD_COLUMN_COUNT) {
return COLUMN_GUTTER_MD;
} else {
return COLUMN_GUTTER_LG;
}
}
/**
* 通过当前栅格数量获取外边距Margin
*
* @param currentColumnCount 当前栅格数量
* @returns 当前栅格外边距
*/
static getColumnMarginVpByColumnCount(currentColumnCount: number) {
if (currentColumnCount === ColumnCount.SM_COLUMN_COUNT) {
return COLUMN_MARGIN_SM;
} else if (currentColumnCount === ColumnCount.MD_COLUMN_COUNT) {
return COLUMN_MARGIN_MD;
} else {
return COLUMN_MARGIN_LG;
}
}
/**
* 根据传入的窗口大小计算单个栅格宽度
*
* @param windowWidth 窗口大小,px
*/
updateColumnWidth(windowWidth: number): void {
if (windowWidth <= 0) {
return;
}
this.singleColumnWidth = (windowWidth - this.columnMargin * DOUBLE - (this.columnCount - 1) * this.columnGutter) /
this.columnCount;
}
}
/**
* 栅格变化监听接口
*/
export interface ColumnChangeListener {
/**
* 栅格变化
*
* @param type 栅格设备类型
* @param count 栅格总数
*/
onColumnChange(type: ColumnDevice, count: number): void;
}
/**
* 栅格设备枚举类,值为断点
*/
export enum ColumnDevice {
SM = 0,
/**
* 低于600vp,4栅格;高于600vp,8栅格
*/
MD = 600,
/**
* 高于1080vp,12栅格
*/
LG = 1080,
}
/**
* 栅格数量枚举,值为具体栅格数量
*/
export enum ColumnCount {
/**
* 手机竖屏,4栅格
*/
SM_COLUMN_COUNT = 4,
/**
* 手机横屏、折叠屏横/竖屏、Pad竖屏,8栅格
*/
MD_COLUMN_COUNT = 8,
/**
* PAD横屏、智慧屏,12栅格
*/
LG_COLUMN_COUNT = 12
}