修复折叠屏合盖旋转不再切换布局的回归
上一版为满足"接近1:1屏幕横竖屏布局一致",把宽屏判据改成纯短边≥840vp, 导致长条形屏幕(普通手机、折叠屏合上后的外屏)旋转横屏也不出双栏。 现在两条判据满足其一即双栏: ① 短边≥840vp(展开态/平板)——旋转不变,维持商店合规要求; ② 横屏且宽高比≥1.5(长条形屏幕)——旋转正常切换,恢复 0.0.2 行为。 判据实现由 mediaquery 改为 windowSizeChange(px→vp),旋转/折叠展开均实时生效, 并记录窗口尺寸日志便于真机排查。
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// entry/src/main/ets/pages/Index.ets
|
// entry/src/main/ets/pages/Index.ets
|
||||||
// 同步日历主界面:月视图(左右滑动翻月)/ 周视图 / 日视图 / 日程列表
|
// 同步日历主界面:月视图(左右滑动翻月)/ 周视图 / 日视图 / 日程列表
|
||||||
// 混合展示 DAV 与系统日历;每分钟自动同步;可"回到今天"
|
// 混合展示 DAV 与系统日历;每分钟自动同步;可"回到今天"
|
||||||
import { curves, mediaquery, router } from '@kit.ArkUI';
|
import { curves, display, router, window } from '@kit.ArkUI';
|
||||||
import { common, abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
|
import { common, abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
|
||||||
import { geoLocationManager } from '@kit.LocationKit';
|
import { geoLocationManager } from '@kit.LocationKit';
|
||||||
import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
|
import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
|
||||||
@@ -54,13 +54,16 @@ struct Index {
|
|||||||
@State syncing: boolean = false;
|
@State syncing: boolean = false;
|
||||||
@State loading: boolean = true;
|
@State loading: boolean = true;
|
||||||
@State menuOpen: boolean = false; // 顶部 ≡ 下拉菜单
|
@State menuOpen: boolean = false; // 顶部 ≡ 下拉菜单
|
||||||
// 宽屏(平板 / 折叠屏展开态):月视图切左右双栏(左月历 / 右当日日程)。
|
// 宽屏(双栏:月视图左月历 / 右当日日程,周视图同理)判据,两条满足其一即双栏:
|
||||||
// 判据是窗口**短边**是否达到大屏断点 840vp:短边在旋转时不变(宽高互换),
|
// ① 窗口短边 ≥ 840vp —— 折叠屏展开态 / 平板这类"大屏"。短边在旋转时不变,
|
||||||
// 所以横竖屏布局天然保持一致 —— 满足"屏幕比例接近 1:1 的设备,横竖屏布局需保持一致";
|
// 故横竖屏布局天然一致,满足"屏幕比例接近 1:1 的设备,横竖屏布局需保持一致";
|
||||||
// 同时不锁死屏幕方向,折叠屏展开态与平板仍可自由横竖屏切换。
|
// ② 横屏且宽高比 ≥ 1.5 —— 长条形屏幕(普通手机、折叠屏**合上后的外屏**)。
|
||||||
|
// 这类设备比例远离 1:1,旋转应当切换布局(0.0.2 起即如此,用户已习惯)。
|
||||||
|
// 加比例下限是为了兜底"短边不足 840 但比例接近 1:1"的小方屏:旋转依旧不变。
|
||||||
|
// 2026-09-29 修复:上一版只留了判据 ①,导致 Mate X7 合盖后旋转屏幕布局不再变化。
|
||||||
@State wideLayout: boolean = false;
|
@State wideLayout: boolean = false;
|
||||||
private widthListener: mediaquery.MediaQueryListener | null = null;
|
private winObj: window.Window | null = null;
|
||||||
private heightListener: mediaquery.MediaQueryListener | null = null;
|
private winSizeCallback: ((size: window.Size) => void) | null = null;
|
||||||
private accounts: DavAccount[] = [];
|
private accounts: DavAccount[] = [];
|
||||||
private sources: CalSource[] = [];
|
private sources: CalSource[] = [];
|
||||||
private swiperController: SwiperController = new SwiperController();
|
private swiperController: SwiperController = new SwiperController();
|
||||||
@@ -147,47 +150,57 @@ struct Index {
|
|||||||
clearInterval(this.nowTimer);
|
clearInterval(this.nowTimer);
|
||||||
this.nowTimer = -1;
|
this.nowTimer = -1;
|
||||||
}
|
}
|
||||||
if (this.widthListener !== null) {
|
if (this.winObj !== null && this.winSizeCallback !== null) {
|
||||||
this.widthListener.off('change');
|
try {
|
||||||
this.widthListener = null;
|
this.winObj.off('windowSizeChange', this.winSizeCallback);
|
||||||
|
} catch (err) {
|
||||||
|
// 窗口可能已销毁(页面退出),忽略
|
||||||
}
|
}
|
||||||
if (this.heightListener !== null) {
|
this.winSizeCallback = null;
|
||||||
this.heightListener.off('change');
|
this.winObj = null;
|
||||||
this.heightListener = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 宽屏判据:窗口短边 min(宽, 高) 是否达到大屏断点 840vp。
|
* 由窗口实际尺寸(px → vp)计算单栏/双栏。判据见 wideLayout 字段注释:
|
||||||
* min(w,h) >= 840vp 等价于 (width >= 840vp) && (height >= 840vp),用两条 mediaquery 合成。
|
* 短边 ≥ 840vp(大屏,旋转不变)或 横屏且宽高比 ≥ 1.5(长条形屏幕,旋转切换)。
|
||||||
*
|
|
||||||
* 为什么用"短边"而不是"是否横屏":
|
|
||||||
* 1) 旋转时短边不变 → 横竖屏布局自动保持一致。屏幕比例接近 1:1 的设备(折叠屏展开态、
|
|
||||||
* 平板)在旋转时不会出现"单栏 ⇄ 双栏"的布局跳变;
|
|
||||||
* 2) 不锁死屏幕方向,仍支持折叠屏展开态与平板的横竖屏自由切换显示。
|
|
||||||
*/
|
*/
|
||||||
private applyLayoutMetrics(): void {
|
private applyLayoutFromWindow(win: window.Window): void {
|
||||||
let wOk: boolean = false;
|
try {
|
||||||
let hOk: boolean = false;
|
const rect = win.getWindowProperties().windowRect;
|
||||||
if (this.widthListener !== null) {
|
const density: number = display.getDefaultDisplaySync().densityPixels;
|
||||||
wOk = this.widthListener.matches;
|
if (density <= 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (this.heightListener !== null) {
|
const w: number = rect.width / density;
|
||||||
hOk = this.heightListener.matches;
|
const h: number = rect.height / density;
|
||||||
|
const shortEdge: number = Math.min(w, h);
|
||||||
|
const ratio: number = h > 0 ? w / h : 1;
|
||||||
|
const wide: boolean = shortEdge >= 840 || (w > h && ratio >= 1.5);
|
||||||
|
LogUtil.write(`窗口尺寸 ${Math.round(w)}x${Math.round(h)}vp(短边 ${Math.round(shortEdge)},宽高比 ${ratio.toFixed(2)})→ ${wide ? '双栏' : '单栏'}`);
|
||||||
|
if (wide !== this.wideLayout) {
|
||||||
|
this.wideLayout = wide;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 取窗口信息失败时保持现状(罕见:窗口已销毁)
|
||||||
}
|
}
|
||||||
this.wideLayout = wOk && hOk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initLayoutListener(): void {
|
private initLayoutListener(): void {
|
||||||
const mq = this.getUIContext().getMediaQuery();
|
const ctx = this.getUIContext().getHostContext();
|
||||||
this.widthListener = mq.matchMediaSync('(width >= 840vp)');
|
if (ctx === undefined) {
|
||||||
this.heightListener = mq.matchMediaSync('(height >= 840vp)');
|
return;
|
||||||
this.applyLayoutMetrics();
|
}
|
||||||
this.widthListener.on('change', (): void => {
|
window.getLastWindow(ctx).then((win: window.Window): void => {
|
||||||
this.applyLayoutMetrics();
|
this.winObj = win;
|
||||||
});
|
this.applyLayoutFromWindow(win);
|
||||||
this.heightListener.on('change', (): void => {
|
this.winSizeCallback = (_size: window.Size): void => {
|
||||||
this.applyLayoutMetrics();
|
// size 参数为 px;重新读取属性并换算(折叠/展开、旋转都会触发)
|
||||||
|
this.applyLayoutFromWindow(win);
|
||||||
|
};
|
||||||
|
win.on('windowSizeChange', this.winSizeCallback);
|
||||||
|
}).catch((err: BusinessError): void => {
|
||||||
|
LogUtil.write(`窗口尺寸监听初始化失败(保持默认单栏):${err.message}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user