鸿蒙:使用断点和媒体查询实现响应式布局
1. 前言
断点,可以理解为划分不同屏幕尺寸的工具,也可以理解为将屏幕尺寸归为一个范围点。做断点的目的是,方便我们在不同屏幕尺寸的鸿蒙设备下实现响应式UI布局。何为响应式布局,我的理解是,屏幕尺寸变化,你的UI会发生变化,以实现最佳的视觉效果,这就是响应式布局。
2. 核心思路
- 用媒体查询接口做横竖屏切换功能。
- 根据官方文档推荐的vp尺寸,合理划分屏幕所处的断点,例如:'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'。
- UI依据断点的变化,布局发生变化。
3. 核心代码
// 获取屏幕纵向断点str
getHeightBreakpointStr() {// HEIGHT_SM 0 窗口高宽比小于0.8。// HEIGHT_MD 1 窗口高宽比大于等于0.8,且小于1.2。// HEIGHT_LG 2 窗口高宽比大于等于1.2。const heightBreakpoint = this.getUIContext().getWindowHeightBreakpoint()console.info(` heightBreakpoint: ${heightBreakpoint}`);switch (heightBreakpoint) {case 0:this.heightBreakpoint = "sm"break;case 1:this.heightBreakpoint = "md"break;case 2:this.heightBreakpoint = "lg"break;default:break;}this.heightBreakpoint
}// 获取屏幕横向断点str
getWidthBreakpointStr() {// 获取当前实例所在窗口的宽度断点枚举值// WIDTH_XS 0 窗口宽度小于320vp。// WIDTH_SM 1 窗口宽度大于等于320vp,且小于600vp。// WIDTH_MD 2 窗口宽度大于等于600vp,且小于840vp。// WIDTH_LG 3 窗口宽度大于等于840vp,且小于1440vp。// WIDTH_XL 4 窗口宽度大于等于1440vp。const widthBreakpoint = this.getUIContext().getWindowWidthBreakpoint()console.info(` widthBreakpoint: ${widthBreakpoint}`);switch (widthBreakpoint) {case 0:this.widthBreakpoint = "xs"break;case 1:this.widthBreakpoint = "sm"break;case 2:this.widthBreakpoint = "md"break;case 3:this.widthBreakpoint = "lg"break;case 4:this.widthBreakpoint = "xl"break;default:break;}}
4. 运行效果
5. 完整代码
Index.ets
import { mediaquery, window } from '@kit.ArkUI';type BreakpointType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';@Entry
@ComponentV2
struct Index {@Local currentState: string = ""listener: mediaquery.MediaQueryListener =this.getUIContext().getMediaQuery().matchMediaSync('(orientation: landscape)');// 获取当前应用的UIAbility上下文context = this.getUIContext().getHostContext()!;@Local widthBreakpoint: BreakpointType = "xs"@Local heightBreakpoint: BreakpointType = "xs"aboutToAppear(): void {// 监听屏幕方向改变this.getCurrentOrientation()}// 获取屏幕纵向断点strgetHeightBreakpointStr() {// HEIGHT_SM 0 窗口高宽比小于0.8。// HEIGHT_MD 1 窗口高宽比大于等于0.8,且小于1.2。// HEIGHT_LG 2 窗口高宽比大于等于1.2。const heightBreakpoint = this.getUIContext().getWindowHeightBreakpoint()console.info(` heightBreakpoint: ${heightBreakpoint}`);switch (heightBreakpoint) {case 0:this.heightBreakpoint = "sm"break;case 1:this.heightBreakpoint = "md"break;case 2:this.heightBreakpoint = "lg"break;default:break;}this.heightBreakpoint}// 获取屏幕横向断点strgetWidthBreakpointStr() {// 获取当前实例所在窗口的宽度断点枚举值// WIDTH_XS 0 窗口宽度小于320vp。// WIDTH_SM 1 窗口宽度大于等于320vp,且小于600vp。// WIDTH_MD 2 窗口宽度大于等于600vp,且小于840vp。// WIDTH_LG 3 窗口宽度大于等于840vp,且小于1440vp。// WIDTH_XL 4 窗口宽度大于等于1440vp。const widthBreakpoint = this.getUIContext().getWindowWidthBreakpoint()console.info(` widthBreakpoint: ${widthBreakpoint}`);switch (widthBreakpoint) {case 0:this.widthBreakpoint = "xs"break;case 1:this.widthBreakpoint = "sm"break;case 2:this.widthBreakpoint = "md"break;case 3:this.widthBreakpoint = "lg"break;case 4:this.widthBreakpoint = "xl"break;default:break;}}/// 获取当前方向状态getCurrentOrientation() {this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {console.log("监听结果" + mediaQueryResult.media)this.getWidthBreakpointStr()this.getHeightBreakpointStr()if (mediaQueryResult.matches) {this.currentState = "横屏"} else {this.currentState = "竖屏"}});}/// 切换横屏switchToLandscape() {// 获取应用的最后一个窗口实例window.getLastWindow(this.context).then((lastWindow) => {// 根据参数设置窗口首选方向:横屏lastWindow.setPreferredOrientation(window.Orientation.LANDSCAPE)});}/// 切换竖屏switchToPortrait() {// 获取应用的最后一个窗口实例window.getLastWindow(this.context).then((lastWindow) => {// 根据参数设置窗口首选方向:竖屏lastWindow.setPreferredOrientation(window.Orientation.PORTRAIT)});}build() {Column({ space: this.currentState == "横屏" ? 20 : 50 }) {Text("响应式布局:断点+媒体查询").fontSize(this.currentState == "横屏" ? 16 : 24).fontWeight(FontWeight.Bold)Column({ space: 30 }) {Text("宽度断点" + this.widthBreakpoint).fontSize(30).fontWeight(FontWeight.Bold)Text("纵向断点" + this.heightBreakpoint).fontSize(30).fontWeight(FontWeight.Bold)Text("当前的状态:" + this.currentState).fontSize(30).fontWeight(FontWeight.Bold)}Column({ space: 20 }) {Button("点击切换横屏").onClick(() => {this.switchToLandscape()}).backgroundColor(this.currentState == "横屏" ? Color.Black : Color.Blue)Button("点击切换竖屏").onClick(() => {this.switchToPortrait()}).backgroundColor(this.currentState == "横屏" ? Color.Black : Color.Blue)}Row({ space: this.widthBreakpoint == "sm" ? 20 : 50 }) {Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)Image($r('app.media.startIcon')).width(this.widthBreakpoint == "sm" ? 20 : 50)}.justifyContent(FlexAlign.Center).width("100%")}.justifyContent(FlexAlign.Center).width("100%").height("100%")}
}
觉得有帮助,可以点赞或收藏