【HarmonyOS NEXT】一次开辟多端摆设(以轮播图、Tab栏、列表为例,配合栅 ...

打印 上一主题 下一主题

主题 858|帖子 858|积分 2574

关键词:一多、响应式、媒体查询、栅格结构、断点、UI

随着设备形态的逐渐增多,应用界面适配也面临着很大题目,在以往的安卓应用开辟过程中,往往需要重新开辟一套适用于大屏展示的应用,耗时又耗力,而鸿蒙提供响应式开辟的解决方案,提供系统级的接口供开辟者调用,从而使得一款应用一套代码能同时运行在不同形态的设备上,也能给用户带来很好的交互体验。

本期文章以轮播图、Tab栏、列表为例,配合栅格结构与媒体查询,举行 UI 的一多开辟。
   本期完整代码已提交至gitee:one2More: 【HarmonyOS NEXT】一次开辟多端摆设(以轮播图、Tab栏、列表为例,配合栅格结构与媒体查询,举行 UI 的一多开辟)
  

目次
效果预览
1. 了解断点、媒体查询、栅格结构
断点
媒体查询
栅格结构
2. 封装媒体查询监听断点工具类
3. 配合媒体查询做 Swiper() 轮播图分割效果
4. 配合媒体查询做 Tab 栏 UI 展示位置变更
5. 配合栅格结构做列表展示数量控制
总结


效果预览


 普通屏
 折叠屏
 大屏

1. 了解断点、媒体查询、栅格结构

断点

鸿蒙提供断点以应用窗口宽度为切入点,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,不同设备会进入到不同的断点区间,在不同的区间下,我们可以可根据需要实现不同的页面结构效果。具体的断点对应的设备尺寸如下所示。
断点名称取值范围(vp)xs[0, 320)sm[320, 600)md[600, 840)lg[840, +∞) 媒体查询

媒体查询支持监听窗口宽度、横竖屏、深浅色、设备范例等多种媒体特征,当媒体特征发生改变时同步调解页面结构。我们可以借助媒体查询本领,监听断点的变革。
栅格结构

栅格组件默认提供xs、sm、md、lg四个断点,除了默认的四个断点,还支持启用 xl 和 xxl 两个额外的断点,我们只需要在 GridRow() 组件的 breakpoints 属性中依次设置对应断点的尺寸,可自行对断点设备的尺寸举行设置从而满足本身尺寸的业务需求,固然还是更推荐使用默认的断点尺,如果使用到媒体查询,和自界说尺寸保持一致即可。
breakpoints 数组中最大可写 5 个尺寸,对应 6 个断点范围,且断点值后面必须加上vp单位。
reference 属性代表 GridRow 宽度变革随屏幕变革,还是随当前局部地区尺寸变革(因为在实际场景中,存在应用窗口尺寸不变但是局部地区尺寸发生了变革的情况,栅格组件支持以自身宽度为参照物响应断点变革具有更大的机动性。)
栅格结构详细介绍请参考:文档中心
示例代码:
  1. @Entry
  2. @Component
  3. struct GridRowSample1 {
  4.   @State private currentBreakpoint: string = 'unknown'
  5.   build() {
  6.     // 修改断点的取值范围同时启用更多断点,注意,修改的断点值后面必须加上vp单位。
  7.     GridRow({breakpoints: {value: ['600vp', '700vp', '800vp', '900vp', '1000vp'],
  8.       reference: BreakpointsReference.WindowSize}}) {
  9.       GridCol({span:{xs: 12, sm: 12, md: 12, lg:12, xl: 12, xxl:12}}) {
  10.         Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
  11.           Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium)
  12.         }
  13.       }
  14.     }.onBreakpointChange((currentBreakpoint: string) => {
  15.       this.currentBreakpoint = currentBreakpoint
  16.     })
  17.   }
  18. }
复制代码

2. 封装媒体查询监听断点工具类

给我们的业务封装媒体查询监听断点工具类,以便后续使用,在首页生命周期过程中举行调用初始化,使用 LocalStorage 或 AppStorage 保存当前屏幕断点的名称,在应用的恣意页面内通过 StorageProp 获取并动态观察屏幕设备形态状态的变革从而更新页面 UI 的展示效果,如折叠屏形态下窄屏变宽屏的使用场景,这时的断点会由 sm 变为 md。
如下代码,监听了 sm、md、lg 三种设备形态,当设备形态变革,会重新写入 storage 数据,配合状态变量即可动态更新 UI。
  1. import mediaQuery from '@ohos.mediaquery';
  2. @Observed
  3. export class MediaWatching {
  4.   private sm: string = '(width<600vp)'
  5.   private md: string = "(600vp<=width<840vp)"
  6.   private lg: string = "(840vp<=width)"
  7.   private type: 'sm' | 'md' | 'lg' = 'sm'
  8.   init() {
  9.     console.log("luvi > mediaQueryResult change " + this.type)
  10.     // 小尺寸屏
  11.     const smListener = mediaQuery.matchMediaSync(this.sm)
  12.     smListener.on('change', (mediaQueryResult) => {
  13.       this.onPortrait(mediaQueryResult)
  14.     })
  15.     // 中等尺寸屏
  16.     const mdListener = mediaQuery.matchMediaSync(this.md)
  17.     mdListener.on('change', (mediaQueryResult) => {
  18.       this.onPortrait(mediaQueryResult)
  19.     })
  20.     // 大尺寸屏
  21.     const lgListener = mediaQuery.matchMediaSync(this.lg)
  22.     lgListener.on('change', (mediaQueryResult) => {
  23.       this.onPortrait(mediaQueryResult)
  24.     })
  25.   }
  26.   onPortrait(mediaQueryResult: mediaQuery.MediaQueryResult) {
  27.     console.log("luvi > mediaQueryResult " + JSON.stringify(mediaQueryResult))
  28.     if (mediaQueryResult.matches) {
  29.       switch (mediaQueryResult.media as string) {
  30.         case this.sm:
  31.           this.type = 'sm'
  32.           break
  33.         case this.md:
  34.           this.type = 'md'
  35.           break
  36.         case this.lg:
  37.           this.type = 'lg'
  38.           break
  39.         default:
  40.           break
  41.       }
  42.       console.log("luvi > mediaQueryResult change " + this.type)
  43.       AppStorage.setOrCreate("currentMediaType", this.type)
  44.     }
  45.   }
  46. }
复制代码


3. 配合媒体查询做 Swiper() 轮播图分割效果

在上一步的媒体查询封装及初始化后,屏幕设备形态变革后的名称会保存在 AppStorage 中,以是我们在自界说组件中可及时获取存入的 currentMediaType ,配合 Swiper 相关接口,使用 .displayCount() 设置 Swiper 视窗内元素表现个数到达分割效果。
  1. @Component
  2. export struct MySwiper {
  3.   swiperList: number[] = [1, 2, 3, 4, 5]
  4.   // 获取当前设备断点形态
  5.   @StorageProp("currentMediaType") currentMediaType: 'sm' | 'md' | 'lg' = 'sm'
  6.   build() {
  7.     Column() {
  8.       Swiper() {
  9.         ForEach(this.swiperList, (item: number, idx: number) => {
  10.           Row() {
  11.             Text(`图片${item}`)
  12.               .textAlign(TextAlign.Center)
  13.               .width("100%")
  14.           }
  15.           .borderRadius(10)
  16.           .width("100%")
  17.           .height(250)
  18.           .backgroundColor(idx % 2 == 0 ? "#ddd" : "#ffb0b0b0")
  19.         })
  20.       }
  21.       .itemSpace(10)
  22.       // 根据断点设置分割个数
  23.       // sm 小屏形态只展示 1 个,其他形态展示 2 个
  24.       .displayCount(this.currentMediaType == "sm" ? 1 : 2)
  25.       .autoPlay(true)
  26.       .borderRadius(10)
  27.       .clip(true)
  28.     }
  29.   }
  30. }
复制代码

4. 配合媒体查询做 Tab 栏 UI 展示位置变更

Tab 栏位置的变革与轮播图分栏同理,配合 storage 状态变量获取设备形态,对不同设备形态更改 Tab 栏标签的排列方向即可。
详细 Tab 代码可参考 gitee 源代码
  1. // 获取当前设备断点形态
  2. @StorageProp("currentMediaType") currentMediaType: 'sm' | 'md' | 'lg' = 'sm'
  3. Tabs({ barPosition: BarPosition.End }) {
  4.     TabContent() {
  5.       // 标签页
  6.       ...
  7.     }
  8.     TabContent() {
  9.       // 标签页
  10.       ...
  11.     }
  12.     TabContent() {
  13.       // 标签页
  14.       ...
  15.     }
  16.   }
  17.   // 对不同设备形态设置不同排列方向
  18.   .vertical(this.currentMediaType == 'sm' || this.currentMediaType == 'md' ? false : true)
复制代码


5. 配合栅格结构做列表展示数量控制

栅格结构拥有独立的断点本领,不依靠与媒体查询接口,以是直接使用栅格结构的特性举行开辟即可。
栅格结构紧张由行和列构成,每一行可以分为若干列,在不同断点状态下展示不同数量的列数,根据这一特性,即可制作成在小屏幕上展示一列文本,在大屏幕上展示两列甚至多列文本。
以是我们可以直接对 GridRow 设置对应断点时展示的 GridCol 个数即可,无需对 GridCol 设置额外参数
  1. GridRow({
  2.    columns: {
  3.      xs: 1, // 超小屏幕 1 列(如手表)
  4.      sm: 1, // 小屏幕 1 列
  5.      md: 2, // 中等屏幕 2 列
  6.      lg: 3  // 大屏幕 3 列
  7.    },
  8.    gutter: 15 // gutter 表示各元素直接间隙尺寸
  9. }) {
  10.    ForEach(this.dataList, (item: number, idx: number) => {
  11.      GridCol() {
  12.        Row() {
  13.          Text(`列表${item}`)
  14.            .textAlign(TextAlign.Center)
  15.            .width("100%")
  16.        }
  17.        .borderRadius(10)
  18.        .width("100%")
  19.        .height(130)
  20.        .backgroundColor("#eee")
  21.      }
  22.    })
  23. }
复制代码

总结

栅格结构拥有独立的断点本领,不依靠与媒体查询接口,以是直接使用栅格结构的特性举行开辟即可。
轮播图的分割效果与 Tab 栏的排列方式变革,同样可使用栅格结构举行实现,不用依靠媒体查询接口,因为栅格结构的 GridRow 组件有 onBreakpointChange 断点变革回调,可直接返回当组件宽度所在的断点区间。
  1. GridRow(){
  2.    ...
  3. }
  4. .onBreakpointChange((bp: string) => {
  5.   // 此处回调打印 xs / sm / md ...
  6. })
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

反转基因福娃

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表