【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词:一多、响应式、媒体查询、栅格布局、断点、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 宽度变化随屏幕变化,还是随当前局部区域尺寸变化(因为在实际场景中,存在应用窗口尺寸不变但是局部区域尺寸发生了变化的情况,栅格组件支持以自身宽度为参照物响应断点变化具有更大的灵活性。)

栅格布局详细介绍请参考:文档中心

示例代码:

@Entry
@Component
struct GridRowSample1 {
  @State private currentBreakpoint: string = 'unknown'
  build() {
    // 修改断点的取值范围同时启用更多断点,注意,修改的断点值后面必须加上vp单位。
    GridRow({breakpoints: {value: ['600vp', '700vp', '800vp', '900vp', '1000vp'],
      reference: BreakpointsReference.WindowSize}}) {
      GridCol({span:{xs: 12, sm: 12, md: 12, lg:12, xl: 12, xxl:12}}) {
        Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
          Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium)
        }
      }
    }.onBreakpointChange((currentBreakpoint: string) => {
      this.currentBreakpoint = currentBreakpoint
    })
  }
}

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

给我们的业务封装媒体查询监听断点工具类,以便后续使用,在首页生命周期过程中进行调用初始化,利用 LocalStorage 或 AppStorage 保存当前屏幕断点的名称,在应用的任意页面内通过 StorageProp 获取并动态观察屏幕设备形态状态的变化从而更新页面 UI 的展示效果,如折叠屏形态下窄屏变宽屏的使用场景,这时的断点会由 sm 变为 md。

如下代码,监听了 sm、md、lg 三种设备形态,当设备形态变化,会重新写入 storage 数据,配合状态变量即可动态更新 UI

import mediaQuery from '@ohos.mediaquery';

@Observed
export class MediaWatching {
  private sm: string = '(width<600vp)'
  private md: string = "(600vp<=width<840vp)"
  private lg: string = "(840vp<=width)"
  private type: 'sm' | 'md' | 'lg' = 'sm'

  init() {
    console.log("luvi > mediaQueryResult change " + this.type)
    // 小尺寸屏
    const smListener = mediaQuery.matchMediaSync(this.sm)
    smListener.on('change', (mediaQueryResult) => {
      this.onPortrait(mediaQueryResult)
    })
    // 中等尺寸屏
    const mdListener = mediaQuery.matchMediaSync(this.md)
    mdListener.on('change', (mediaQueryResult) => {
      this.onPortrait(mediaQueryResult)
    })
    // 大尺寸屏
    const lgListener = mediaQuery.matchMediaSync(this.lg)
    lgListener.on('change', (mediaQueryResult) => {
      this.onPortrait(mediaQueryResult)
    })
  }

  onPortrait(mediaQueryResult: mediaQuery.MediaQueryResult) {
    console.log("luvi > mediaQueryResult " + JSON.stringify(mediaQueryResult))
    if (mediaQueryResult.matches) {
      switch (mediaQueryResult.media as string) {
        case this.sm:
          this.type = 'sm'
          break
        case this.md:
          this.type = 'md'
          break
        case this.lg:
          this.type = 'lg'
          break
        default:
          break
      }
      console.log("luvi > mediaQueryResult change " + this.type)
      AppStorage.setOrCreate("currentMediaType", this.type)
    }
  }
}

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

在上一步的媒体查询封装及初始化后,屏幕设备形态变化后的名称会保存在 AppStorage 中,所以我们在自定义组件中可及时获取存入的 currentMediaType ,配合 Swiper 相关接口,使用 .displayCount() 设置 Swiper 视窗内元素显示个数达到分割效果。

@Component
export struct MySwiper {
  swiperList: number[] = [1, 2, 3, 4, 5]
  // 获取当前设备断点形态
  @StorageProp("currentMediaType") currentMediaType: 'sm' | 'md' | 'lg' = 'sm'

  build() {
    Column() {
      Swiper() {
        ForEach(this.swiperList, (item: number, idx: number) => {
          Row() {
            Text(`图片${item}`)
              .textAlign(TextAlign.Center)
              .width("100%")
          }
          .borderRadius(10)
          .width("100%")
          .height(250)
          .backgroundColor(idx % 2 == 0 ? "#ddd" : "#ffb0b0b0")
        })
      }
      .itemSpace(10)
      // 根据断点设置分割个数
      // sm 小屏形态只展示 1 个,其他形态展示 2 个
      .displayCount(this.currentMediaType == "sm" ? 1 : 2)
      .autoPlay(true)
      .borderRadius(10)
      .clip(true)
    }
  }
}

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

Tab 栏位置的变化与轮播图分栏同理,配合 storage 状态变量获取设备形态,对不同设备形态更改 Tab 栏标签的排列方向即可。

详细 Tab 代码可参考 gitee 源代码

// 获取当前设备断点形态
@StorageProp("currentMediaType") currentMediaType: 'sm' | 'md' | 'lg' = 'sm'

Tabs({ barPosition: BarPosition.End }) {
    TabContent() {
      // 标签页
      ...
    }

    TabContent() {
      // 标签页
      ...
    }

    TabContent() {
      // 标签页
      ...
    }
  }
  // 对不同设备形态设置不同排列方向
  .vertical(this.currentMediaType == 'sm' || this.currentMediaType == 'md' ? false : true)

5. 配合栅格布局做列表展示数量控制

栅格布局拥有独立的断点能力,不依赖与媒体查询接口,所以直接使用栅格布局的特性进行开发即可。

栅格布局主要由行和列组成,每一行可以分为若干列,在不同断点状态下展示不同数量的列数,根据这一特性,即可制作成在小屏幕上展示一列文本,在大屏幕上展示两列甚至多列文本。

所以我们可以直接对 GridRow 设置对应断点时展示的 GridCol 个数即可,无需对 GridCol 设置额外参数

GridRow({
   columns: {
     xs: 1, // 超小屏幕 1 列(如手表)
     sm: 1, // 小屏幕 1 列
     md: 2, // 中等屏幕 2 列
     lg: 3  // 大屏幕 3 列
   },
   gutter: 15 // gutter 表示各元素直接间隙尺寸
 }) {
   ForEach(this.dataList, (item: number, idx: number) => {
     GridCol() {
       Row() {
         Text(`列表${item}`)
           .textAlign(TextAlign.Center)
           .width("100%")
       }
       .borderRadius(10)
       .width("100%")
       .height(130)
       .backgroundColor("#eee")
     }
   })
 }

总结

栅格布局拥有独立的断点能力,不依赖与媒体查询接口,所以直接使用栅格布局的特性进行开发即可。

轮播图的分割效果与 Tab 栏的排列方式变化,同样可使用栅格布局进行实现,不用依赖媒体查询接口,因为栅格布局的 GridRow 组件有 onBreakpointChange 断点变化回调,可直接返回当组件宽度所在的断点区间。

GridRow(){
   ...
}
.onBreakpointChange((bp: string) => {
  // 此处回调打印 xs / sm / md ...
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/913086.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux 安装 mongodb

选择MongoDB版本 https://www.mongodb.com/try/download/community-kubernetes-operator 我的系统是centos7.9 这里只能最高只能选择mongo7 复制下载链接&#xff1a;https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.15.tgz 获取安装教程&#xff1a; h…

《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析

导读&#xff1a;SQL 诞生于 20 世纪 70 年代&#xff0c;至今已有半个世纪。SQL 语言具有语法简单&#xff0c;低学习门槛等特点&#xff0c;诞生之后迅速普及与流行开来。由于 SQL 具有易学易用的特点&#xff0c;使得开发人员容易掌握&#xff0c;企业若能在其计算机软件中支…

PostgreSQL pg-xact(clog)目录文件缺失处理

一、 背景 前些天晚上突然收到业务反馈&#xff0c;查询DB中的一个表报错 Could not open file "pg-xact/005E": No such file or directory. 两眼一黑难道是文件损坏了...登录查看DB日志&#xff0c;还好没有其他报错&#xff0c;业务也反馈只有这一个表在从库查询报…

Cursor的chat与composer的使用体验分享

经过一段时间的试用&#xff0c;下面对 Composer 与 Chat 的使用差别进行总结&#xff1a; 一、长文本及程序文件处理方面 Composer 在处理长文本时表现较为稳定&#xff0c;可以对长文进行更改而不会出现内容丢失的情况。而 Chat 在更改长的程序文件时&#xff0c;有时会删除…

MATLAB课程:AI工具辅助编程——MATLAB+LLMs

给出一些可能有用的方法辅助大家写代码。 方法一&#xff1a;MATLAB软件LLM (不太懂配置的同学们为了省事可以主要用这个方法) 方法一特别针对本门MATLAB教学课程&#xff0c;给出一种辅助ai工具的操作指南。MATLAB中可以安装MatGPT插件&#xff0c;该插件通过调用ChatGPT的API…

2.索引:SQL 性能分析详解

SQL性能分析是数据库优化中重要的一环。通过分析SQL的执行频率、慢查询日志、PROFILE工具以及EXPLAIN命令&#xff0c;能够帮助我们识别出数据库性能的瓶颈&#xff0c;并做出有效的优化措施。以下将详细讲解这几种常见的SQL性能分析工具和方法。 一、SQL 执行频率 SQL执行频率…

使用Go语言编写一个简单的NTP服务器

NTP服务介绍 NTP服务器【Network Time Protocol&#xff08;NTP&#xff09;】是用来使计算机时间同步化的一种协议。 应用场景说明 为了确保封闭局域网内多个服务器的时间同步&#xff0c;我们计划部署一个网络时间同步服务器&#xff08;NTP服务器&#xff09;。这一角色将…

深度学习经典模型之VGGNet

1 VGGNet 1.1 模型介绍 ​ VGGNet是由牛津大学视觉几何小组&#xff08;Visual Geometry Group, VGG&#xff09;提出的一种深层卷积网络结构&#xff0c;他们以7.32%的错误率赢得了2014年ILSVRC分类任务的亚军&#xff08;冠军由GoogLeNet以6.65%的错误率夺得&#xff09;和…

Android的BroadcastReceiver

1.基本概念&#xff1a;BroadCast用于进程间或者线程间通信 本质上是用Binder方法&#xff0c;以AMS为订阅中心&#xff0c;完成注册&#xff0c;发布&#xff0c;监听的操作。 2.简单实现的例子 package com.android.car.myapplication;import android.content.BroadcastRe…

分布式数据库中间件mycat

MyCat MyCat是一个开源的分布式数据库系统&#xff0c;它实现了MySQL协议&#xff0c;可以作为数据库代理使用。 MyCat(中间件)的核心功能是分库分表&#xff0c;即将一个大表水平分割为多个小表&#xff0c;存储在后端的MySQL服务器或其他数据库中。 它不仅支持MySQL&#xff…

Java多线程编程(四)- 阻塞队列,生产者消费者模型,线程池

目录&#xff1a; 一.阻塞队列 二.线程池 一.阻塞队列 1.阻塞队列是⼀种特殊的队列. 也遵守 "先进先出" 的原则 阻塞队列能是⼀种线程安全的数据结构, 并且具有以下特性&#xff1a; 1.1.当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素 1.…

深度剖析JUC中LongAdder类源码

文章目录 1.诞生背景2.LongAdder核心思想3.底层实现&#xff1a;4.额外补充 1.诞生背景 LongAdder是JDK8新增的一个原子操作类&#xff0c;和AtomicLong扮演者同样的角色&#xff0c;由于采用AtomicLong 保证多线程数据同步&#xff0c;高并发场景下会导致大量线程同时竞争更新…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…

树形结构数据

树形结构数据 树形结构数据是一种基础且强大的数据结构&#xff0c;广泛应用于计算机科学和软件开发的各个领域。它模拟了自然界中树的层级关系&#xff0c;通过节点和它们之间的连接来组织数据。在本文中&#xff0c;我们将深入探讨树形结构数据的概念、特点、类型以及它们在…

dell服务器安装ESXI8

1.下载镜像在官网 2.打开ipmi&#xff08;idrac&#xff09;&#xff0c;将esxi镜像挂载&#xff0c;然后服务器开机 3.进入bios设置cpu虚拟化开启&#xff0c;进入boot设置启动选项为映像方式 4..进入安装引导界面3.加载完配置进入安装 系统提示点击继 5.选择安装磁盘进行…

信息安全数学基础(46)域和Galois理论

域详述 定义&#xff1a; 域是一个包含加法、减法、乘法和除法&#xff08;除数不为零&#xff09;的代数结构&#xff0c;其中加法和乘法满足交换律、结合律&#xff0c;并且乘法对加法满足分配律。同时&#xff0c;域中的元素&#xff08;通常称为数&#xff09;在加法和乘法…

Windows端口占用/Java程序启动失败-进程占用的问题解决

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Python酷库之旅-第三方库Pandas(204)

目录 一、用法精讲 951、pandas.IntervalIndex.values属性 951-1、语法 951-2、参数 951-3、功能 951-4、返回值 951-5、说明 951-6、用法 951-6-1、数据准备 951-6-2、代码示例 951-6-3、结果输出 952、pandas.IntervalIndex.from_arrays类方法 952-1、语法 952…

AndroidStudio-文本显示

一、设置文本的内容 1.方式&#xff1a; &#xff08;1&#xff09;在XML文件中通过属性&#xff1a;android:text设置文本 例如&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.andr…