概述
媒体查询(@ohos.mediaquery)作为响应式设计的核心,在移动设备上应用十分广泛。媒体查询可根据不同设备类型或同设备不同状态修改应用的样式。媒体查询常用于下面两种场景:
-
针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
-
当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。
核心用法
// 1. 导入 模块
import mediaquery from '@ohos.mediaquery';
// 2. 创建监听器
const listenerXS: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<=width<320vp)');
const listenerSM: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(320vp<=width<600vp)');
// 3. 注册监听器
// 组件即将创建出来
aboutToAppear(): void {
// 添加回调函数
listenerXS.on('change', (res: mediaquery.MediaQueryResult) => {
console.log('changeRes:', JSON.stringify(res))
// 执行逻辑
})
listenerSM.on('change', (res: mediaquery.MediaQueryResult) => {
console.log('changeRes:', JSON.stringify(res))
// 执行逻辑
})
}
// 4. 移除监听器
// 即将销毁
aboutToDisappear(): void {
// 移除监听 避免性能浪费
listenerXS.off('change')
listenerSM.off('change')
}
下例中使用媒体查询,实现屏幕横竖屏切换时,给页面文本应用添加不同的内容和样式。
Stage模型下的示例
import mediaquery from '@ohos.mediaquery';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
@Entry
@Component
struct MediaQueryExample {
@State color: string = '#DB7093';
@State text: string = 'Portrait';
@State portraitFunc:mediaquery.MediaQueryResult|void|null = null;
// 当设备横屏时条件成立
listener:mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(orientation: landscape)');
// 当满足媒体查询条件时,触发回调
onPortrait(mediaQueryResult:mediaquery.MediaQueryResult) {
if (mediaQueryResult.matches as boolean) { // 若设备为横屏状态,更改相应的页面布局
this.color = '#FFD700';
this.text = 'Landscape';
} else {
this.color = '#DB7093';
this.text = 'Portrait';
}
}
aboutToAppear() {
// 绑定当前应用实例
// 绑定回调函数
this.listener.on('change', (mediaQueryResult:mediaquery.MediaQueryResult) => { this.onPortrait(mediaQueryResult) });
}
// 改变设备横竖屏状态函数
private changeOrientation(isLandscape: boolean) {
// 获取UIAbility实例的上下文信息
let context:common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
// 调用该接口手动改变设备横竖屏状态
window.getLastWindow(context).then((lastWindow) => {
lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT)
});
}
build() {
Column({ space: 50 }) {
Text(this.text).fontSize(50).fontColor(this.color)
Text('Landscape').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
this.changeOrientation(true);
})
Text('Portrait').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
.onClick(() => {
this.changeOrientation(false);
})
}
.width('100%').height('100%')
}
}
效果图
BreakPointSystem工具类
断点
断点以应用窗口宽度为切入点,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,在不同的区间下,开发者可根据需要实现不同的页面布局效果。具体的断点如下所示。
断点名称 | 取值范围(vp) |
---|---|
xs | [0, 320) |
sm | [320, 600) |
md | [600, 840) |
lg | [840, +∞) |
对通过媒体查询监听断点的功能做简单的封装,方便后续使用
// common/breakpointsystem.ets
import mediaQuery from '@ohos.mediaquery'
declare interface BreakPointTypeOption<T> {
xs?: T
sm?: T
md?: T
lg?: T
xl?: T
xxl?: T
}
export class BreakPointType<T> {
options: BreakPointTypeOption<T>
constructor(option: BreakPointTypeOption<T>) {
this.options = option
}
getValue(currentBreakPoint: string) {
if (currentBreakPoint === 'xs') {
return this.options.xs
} else if (currentBreakPoint === 'sm') {
return this.options.sm
} else if (currentBreakPoint === 'md') {
return this.options.md
} else if (currentBreakPoint === 'lg') {
return this.options.lg
} else if (currentBreakPoint === 'xl') {
return this.options.xl
} else if (currentBreakPoint === 'xxl') {
return this.options.xxl
} else {
return undefined
}
}
}
interface Breakpoint {
name: string
size: number
mediaQueryListener?: mediaQuery.MediaQueryListener
}
export class BreakpointSystem {
private currentBreakpoint: string = 'md'
private breakpoints: Breakpoint[] = [
{ name: 'xs', size: 0 }, { name: 'sm', size: 320 },
{ name: 'md', size: 600 }, { name: 'lg', size: 840 }
]
private updateCurrentBreakpoint(breakpoint: string) {
if (this.currentBreakpoint !== breakpoint) {
this.currentBreakpoint = breakpoint
AppStorage.Set<string>('currentBreakpoint', this.currentBreakpoint)
console.log('on current breakpoint: ' + this.currentBreakpoint)
}
}
public register() {
this.breakpoints.forEach((breakpoint: Breakpoint, index) => {
let condition:string
if (index === this.breakpoints.length - 1) {
condition = '(' + breakpoint.size + 'vp<=width' + ')'
} else {
condition = '(' + breakpoint.size + 'vp<=width<' + this.breakpoints[index + 1].size + 'vp)'
}
console.log(condition)
breakpoint.mediaQueryListener = mediaQuery.matchMediaSync(condition)
breakpoint.mediaQueryListener.on('change', (mediaQueryResult) => {
if (mediaQueryResult.matches) {
this.updateCurrentBreakpoint(breakpoint.name)
}
})
})
}
public unregister() {
this.breakpoints.forEach((breakpoint: Breakpoint) => {
if(breakpoint.mediaQueryListener){
breakpoint.mediaQueryListener.off('change')
}
})
}
}