HarmonyOS学习第13天:布局进阶,从嵌套到优化

布局嵌套初体验

在 HarmonyOS 应用开发中,布局嵌套是构建复杂界面的重要手段。就像搭建一座高楼,布局嵌套能让各个界面元素有序组合,构建出功能丰富、层次分明的用户界面。我们以日常使用的电商 APP 为例,在商品展示区,为了清晰展示商品信息,就会用到布局嵌套。比如,商品图片需要有固定的尺寸和位置,价格和商品名称需要在合适的地方展示,这些元素组合起来,就构成了一个商品展示单元。这个展示单元可能被放置在一个 Grid 布局中,方便以网格形式整齐排列多个商品。

在 HarmonyOS 中,Grid 布局就像一个规整的棋盘,我们可以把商品展示单元看作棋盘上的棋子,通过设置 Grid 布局的行数、列数以及每个单元格的大小,将商品展示单元合理放置其中 。而在每个商品展示单元内部,又会有更细致的布局。商品图片可能被放在一个单独的容器布局中,设置好宽高比,确保图片展示的完整性;价格和商品名称则会放在线性布局中,通过设置垂直或水平排列,让它们在视觉上与图片搭配协调。这样层层嵌套的布局,能使整个商品展示区既美观又高效。

布局嵌套的核心思路,就是将大的布局拆分成多个小的布局模块,每个模块负责展示一部分内容,然后再将这些小模块组合成一个完整的界面。这样做的好处显而易见,一方面可以提高代码的可维护性,当某个部分需要修改时,只需要关注对应的小布局模块,而不会影响到整个界面;另一方面,通过合理的布局嵌套,可以让界面元素的排列更加灵活,满足不同的设计需求。比如,在电商 APP 中,不同种类的商品可能有不同的展示方式,通过布局嵌套,我们可以轻松实现多样化的展示效果。

常见布局嵌套案例剖析

Scroll 与 Web、List 的嵌套

在 HarmonyOS 应用开发中,Scroll、Web 和 List 组件的嵌套使用十分常见。以新闻浏览界面为例,在这个界面中,我们可能会用 Scroll 作为父容器,实现整个页面的滚动功能;用 Web 组件来展示新闻的详细内容,因为 Web 组件可以很好地解析和展示富文本格式的新闻;而 List 组件则用于展示新闻的评论区,方便用户查看和参与评论 。

然而,当这三个组件嵌套在一起时,很容易出现滚动手势冲突的问题。比如,当用户想要滚动页面查看新闻内容时,可能会意外触发 Web 组件或 List 组件的滚动,导致操作不流畅。为了解决这个问题,我们可以采取一些策略。一种有效的方法是禁用子组件(Web 和 List)的滚动手势,让父组件 Scroll 统一控制滚动。具体实现时,我们可以在 Web 组件加载完成后,通过调用setScrollable(false, webview.ScrollType.EVENT)方法来禁用 Web 组件手势生成的滚动 。同时,使用onGestureRecognizerJudgeBegin方法,禁用 Web 自带的 pan 手势触发,即鼠标滚轮和触摸板的双指滑动。对于 List 组件,也可以通过enableScrollInteraction(false)方法来禁用其手势。

仅仅禁用子组件的滚动手势还不够,还需要父组件 Scroll 统一计算并派发偏移量,以实现 Web 组件和 List 组件滚动的无缝衔接,就像同一个滚动组件在滚动一样。在 Scroll 的onScrollFrameBegin回调函数中,我们可以获取 Scroll 返回的实际滚动量offset。当手指向上划动(页面下滑)时,如果 Web 没有滚动到底部,就将滚动偏移量派发给 Web,Scroll 组件自身不滚动;如果 Web 滚动到底部,且 Scroll 没有滚动到底部,则 Scroll 自身滚动,不给 Web 和 List 派发滚动偏移量;如果 Scroll 滚动到底部,则将滚动偏移量派发给 List,Scroll 自身不滚动 。当手指向下划动(页面上滑)时,逻辑则相反。通过这样的方式,就能很好地解决 Scroll 与 Web、List 嵌套时的滚动手势冲突问题,提升用户体验。

Grid 与 List、Swiper 的嵌套

Grid 与 List 相互嵌套

在第三方服务目录页面的开发中,Grid 与 List 的嵌套可以实现非常实用的二级联动效果。页面上方通过 List 横向布局展示服务目录,下方则通过 List 展示详细的功能属性模块列表 。当用户滑动上方的目录 List 时,下方的功能列表也会相应地滚动到对应的位置,实现二级联动。

为了实现这一效果,我们需要借助一些事件和方法。通过onScrollFrameBegin事件可以计算实时滚动量,当页面滑动时,使上方的精选布局滚动。如果页面已滚动到底部,且列表不在顶部或有正向偏移量,则使页面上方精选部分自动上滑,功能列表置顶。同时,通过ForEach遍历出来的功能目录和详细菜单列表,由于目录与内容两者的index值一致,两个分属不同的 List 列表,可以关联不同的Scroll值。我们可以通过scrollToIndex方法来使两个列表形成二级联动效果 。例如,当用户点击上方目录中的某个选项时,通过scrollToIndex方法可以让下方功能列表快速滚动到对应的位置,提高用户查找信息的效率。

Grid 与 Swiper 相互嵌套

在实现分页效果布局时,Grid 与 Swiper 的嵌套使用能发挥出很好的作用。比如,在一个包含众多功能菜单的页面中,为了避免页面过于冗长,我们可以将功能菜单分页展示。每一页中包含的功能菜单数量存储至分页数组gridColList1 。通过多层遍历存放 Grid 容器组件的自定义组件来达到分页效果。

具体实现过程中,我们可以在 Swiper 组件中嵌套 Grid 组件,每个 Grid 组件代表一页的内容。通过设置 Swiper 的相关属性,如indicator(指示点)、autoplay(自动播放)等,可以让分页效果更加明显和易用。当用户滑动 Swiper 时,会切换不同的 Grid 页面,实现分页浏览的功能。同时,还可以根据实际需求,动态修改 Swiper 组件的高度,以适应不同的屏幕尺寸和布局要求。这样,通过 Grid 与 Swiper 的嵌套,我们就能轻松实现美观且实用的分页效果布局。

布局嵌套带来的挑战

虽然布局嵌套为界面设计带来了极大的灵活性,但当布局嵌套过深时,也会带来一系列的挑战,其中最突出的就是对 UI 渲染性能的负面影响。

在 HarmonyOS 中,当布局嵌套层数过多,每一层布局组件都会参与父组件的大小计算,这使得 UI 渲染过程变得异常复杂 。每次 UI 发生更新,比如用户点击按钮、界面切换等操作,系统都需要重新计算所有父子组件的大小和位置。这就好比在一场接力赛中,每个运动员都需要花费大量时间交接棒,导致整个比赛进程缓慢。在布局计算中,随着嵌套层级的增加,每个层级在测量(measure)和布局(layout)过程中都会递归地调用其子视图的相同方法,导致计算量呈指数级增长。这种频繁的布局计算不仅增加了 CPU 的负担,消耗大量的 CPU 资源,还会使应用的响应速度变慢,用户操作时可能会感觉到明显的卡顿 。

布局嵌套过深还会导致内存占用过高。每个嵌套的视图或布局都需要在内存中分配一定的空间来存储其状态和属性。当嵌套层次过深或嵌套的视图数量过多时,内存占用将显著增加。就像一个房间里堆放了过多的物品,导致空间变得拥挤,影响了正常的活动。在应用中,过多的内存占用可能会导致应用运行缓慢,甚至出现卡顿或崩溃的情况。此外,GPU 在渲染复杂布局时也会受到影响。过多的嵌套布局可能会增加渲染的复杂度,导致渲染性能下降,进而引起卡顿现象 。

滑动冲突也是布局嵌套中常见的问题,当两个或多个具有滑动功能的组件嵌套在一起时,比如 ScrollView 嵌套 ListView,就容易出现滑动冲突。用户在操作时,可能会发现滑动的效果不如预期,甚至出现界面响应不及时的情况。这种滑动冲突不仅影响了用户体验,还可能导致应用的可用性降低。同时,嵌套布局中的交互元素,如按钮、输入框等,也可能会因为布局计算或绘制的延迟而导致交互响应不及时,给用户带来不好的使用感受 。

布局优化策略大揭秘

扁平化布局结构

为了减少布局嵌套带来的性能问题,我们可以采用扁平化布局结构。在实际开发中,尽量使用 Row、Column、Flex 等容器代替复杂的 Stack、Container 等布局,从而减少不必要的嵌套层级。

以一个简单的登录界面为例,假设最初的布局使用了多层嵌套的 Container 和 Row,代码如下:

Container()

 .backgroundColor(Color.White)

 .width('100%')

 .height('100%')

 .child(

      Container()

       .width('80%')

       .height('60%')

       .margin({ top: '20%', left: '10%' })

       .child(

            Row()

             .width('100%')

             .height('100%')

             .child(

                  Column()

                   .width('100%')

                   .height('100%')

                   .spacing('20px')

                   .child(Text('用户名'))

                   .child(TextField())

                   .child(Text('密码'))

                   .child(TextField())

                   .child(Button('登录'))

              )

          )

    )

在这段代码中,我们使用了多层 Container 和 Row 进行布局,这会增加布局的复杂度和计算量。通过扁平化布局,我们可以将其优化为:

Column()

 .width('100%')

 .height('100%')

 .alignItems(HorizontalAlign.Center)

 .justifyContent(FlexAlign.Center)

 .spacing('20px')

 .child(Text('用户名'))

 .child(TextField())

 .child(Text('密码'))

 .child(TextField())

 .child(Button('登录'))

优化后的代码直接使用 Column 进行布局,去掉了多余的 Container 和 Row,使布局结构更加简洁明了。这样做不仅减少了布局的嵌套层级,降低了布局计算的复杂度,还提高了代码的可读性和可维护性。在 UI 渲染时,由于减少了计算量,渲染速度也会得到提升,从而提高了应用的性能和用户体验 。

合理使用动画

动画的影响

动画是提升 UI 体验的有效手段,但不合理或过度使用动画会导致应用的 UI 性能下降,甚至产生卡顿现象。特别是在较老的设备上,复杂或频繁的动画可能会占用过多的 CPU 和 GPU 资源,影响应用的流畅性。每次动画执行时,系统需要重新计算组件的位置、大小或透明度等属性,并重新渲染界面。如果动画过多,特别是在屏幕上有多个动画同时执行时,会增加 UI 线程的负担,造成 UI 线程的阻塞和应用响应的延迟 。

优化策略

为了避免动画对性能造成过大影响,我们可以采取一些优化策略。首先,要优化动画的执行时机,动画不应过多地影响 UI 的交互响应,将动画分散执行,避免动画与用户操作同时进行 。例如,在一个电商 APP 中,当用户点击商品图片查看详情时,如果同时触发多个复杂的动画,如图片的放大缩小、文字的淡入淡出等,会导致界面卡顿,影响用户体验。我们可以将这些动画分阶段执行,先展示商品图片的放大效果,待用户操作稳定后,再执行文字的淡入动画。

其次,HarmonyOS 支持 GPU 加速动画,尽量使用硬件加速的属性变更(如 transform、opacity 等),避免使用不支持硬件加速的属性(如 top、left) 。以一个简单的图片旋转动画为例,使用硬件加速的transform属性实现动画的代码如下:

@Entry

@Component

struct AnimatedComponent {

  @State rotation: number = 0;

  build() {

    Column()

   .width('100%')

   .height('100%')

   .justifyContent(FlexAlign.Center)

   .alignItems(HorizontalAlign.Center)

   .child(

        Image($r('app.media.example'))

       .width('200px')

       .height('200px')

       .transform({ rotate: this.rotation })

       .animation({

            duration: 2000,

            curve: Curve.Linear,

            iterations: -1

          })

      )

   .child(

        Button('开始动画')

       .onClick(() => {

            this.rotation += 360;

          })

      );

  }

}

在这个例子中,通过修改transform属性的rotate值来实现图片的旋转动画,并且使用了animation来控制动画的时长、曲线和迭代次数。由于transform属性支持硬件加速,这个动画在执行时会更加流畅,减少了对 UI 线程的负担 。

还可以使用 Tween 动画替代过渡动画,Tween 动画通过插值函数平滑过渡,能减少频繁的重绘和布局计算,提高动画性能 。比如,在一个页面切换的动画中,使用 Tween 动画可以让页面的过渡更加自然流畅,同时减少性能消耗。

其他优化技巧

指定组件宽高

对于组件的宽高不需要自适应的情况下,建议在 UI 描述时给定组件的宽高数值。当组件外部的容器尺寸发生变化时,例如拖拽缩放等场景下,如果组件本身的宽高是固定的,理论上来讲,该组件在布局阶段不会参与 Measure 阶段,其节点中保存了对应的大小信息,如果组件内容较多时,由于避免了其中组件整体的测算过程,性能会带来较大的提升 。例如,在一个展示商品图片的界面中,如果图片的大小是固定的,我们可以直接指定图片组件的宽高,这样在界面布局计算时,就不需要反复计算图片的大小,提高了布局计算的效率。

合理控制组件显示与隐藏

控制元素显示与隐藏是一种常见的场景,使用 Visibility.None、if 条件判断等都能够实现该效果 。其中 if 条件判断控制的是组件的创建、布局阶段,visibility 属性控制的是元素在布局阶段是否参与布局渲染 。如果只有初始的一次渲染或者交互次数很少的情况下,建议使用 if 条件判断来控制元素的显示与隐藏效果,对于内存有较大提升。用 if 条件判断切换显示时,组件会因为条件改变而判断是否参与创建、布局过程,切换过程会出现较大的 Measure 的性能消耗,原因是创建了新的组件,重新进行了 Measure 和 Layout 的过程。如果会频繁响应显示与隐藏的交互效果,建议使用切换 Visibility.None 和 Visibility.Visible 来控制元素显示与隐藏,提高性能。使用 visibility 的情况下,无论是否隐藏,组件在初次已经创建完成,并一直都存在组件树上,不会出现组件重新创建的过程,并且在 Measure 和 Layout 阶段的性能消耗比使用 if/else 的方式性能小很多,原因是组件的计算在首帧时已经计算过,不需要重复计算。

选择合适的布局组件

在布局深度和节点数相同的情况下,使用基础组件如 Column 和 Row 容器的性能明显高于其他布局,Flex 的性能明显低于 Column 和 Row 容器,这是由于 Flex 本身带来的二次布局的影响 。Grid/GridItem 布局、相对布局 RelativeContainer 的性能消耗高于基础组件 Column、Row、Stack 等容器 。在使用布局时尽量遵循以下原则:在相同嵌套层级的情况下,如果多种布局方式可以实现相同布局效果,优选低耗时的布局,如使用 Column、Row 替代 Flex 实现相同的单行布局 。在能够通过其他布局大幅优化节点数的情况下,可以使用高级组件替代,如使用 RelativeContainer 替代 Row、Column 实现扁平化布局,此时其收益大于布局组件本身的性能差距 。仅在必要的场景下使用高耗时的布局组件,如使用 Flex 实现折行布局、使用 Grid 实现二维网格布局等 。例如,在一个简单的水平排列的按钮组中,使用 Row 布局就比使用 Flex 布局性能更高,因为 Row 布局相对简单,计算量小;而在一个需要实现复杂的自适应布局的场景中,可能就需要使用 RelativeContainer 来优化布局结构,虽然 RelativeContainer 的性能消耗相对较高,但通过优化节点数可以带来更大的性能提升。

实践与总结

HarmonyOS 布局的嵌套与优化是一项充满挑战但又极具价值的工作,它需要我们在实践中不断摸索和总结经验。希望大家在学习和实践过程中,积极动手尝试,将理论知识转化为实际的应用能力 。

如果你在实践过程中有任何疑问、心得或者有趣的案例,欢迎在评论区留言分享。让我们一起在 HarmonyOS 的学习道路上,共同进步,打造出更加美观高效的应用界面 !

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

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

相关文章

基于机器学习的智能谣言检测系统

在信息爆炸的时代,谣言和虚假信息的传播速度比以往任何时候都快。为了应对这一挑战,我们可以利用机器学习技术构建一个智能谣言检测系统。本文将带你从零开始,使用 Python 和 PyQt5 构建一个功能强大的谣言检测工具,并深入探讨其背…

数据结构——单调栈

一.单调栈简介 1.1单调栈定义与特性 本质:单调栈是一种特殊的栈结构,其内部元素始终保持单调递增或单调递减的顺序。核心规则:当新元素入栈时,会通过弹出破坏单调性的栈顶元素来维持有序性。单调方向: 单调递增栈&…

网络编程相关概念

一 网络概念 1.国际网络体系结构: OSI模型: open system interconnect 理论模型 1977 国际标准化组织 各种不同体系结构的计算机能在世界范围内互联成网。 OSI模型 应用层:要传输的数据信息,如文件传输,电子…

Trae:国内首款AI原生IDE,编程效率大提升

今年一月,在新闻上看到字节跳动面向海外市场推出了一款名为Trae的AI集成开发环境(IDE)。起初,我并未给予过多关注,因为市面上已有不少IDE集成了AI插件,功能也非常全面,而字节跳动自家的MarsCode…

Metasploit multi/handler 模块高级选项解析

multi/handler 是 Metasploit 框架中至关重要的模块,主要用于监听目标机的连接并处理来自目标的反向 shell 或会话。它可以灵活地适应不同渗透测试场景,提供高度的自定义选项以优化监听器的行为。 在 Metasploit msf6 框架中,当使用 exploit…

【前端】在WebStorm中安装Node.js与nvm与npm的详细过程

文章目录 一、Node.js安装二、nvm安装三、验证安装成功总结 一、Node.js安装 首先到node.js官网下载安装文件。 https://nodejs.org/zh-cn 直接运行安装文件进行安装: 跳过继续安装: 完成安装: 完成后的安装路径: 环境变量的…

广域互联方案与技术概述

《广域互联方案与技术概述》属于博主的“广域网”专栏,若想成为HCIE,对于广域网相关的知识需要非常了解,更多关于广域网的内容博主会更新在“广域网”专栏里,请持续关注! 一.前言 广域网有着悠久的历史,广…

华硕电脑开启电池保养模式的方法

华硕电脑开启电池保养模式的方法 打开华硕电脑管家(可以桌面左下角搜索MyASUS打开)进入首页(可以不注册,点击跳过,进入首页),点击电池: 之后在新的页面点击电池保养模式: 开启电池保养模式

一键安装Mysql部署脚本之Linux在线安装Mysql,脚本化自动化执行服务器部署(附执行脚本下载)

相关链接 一键安装Redis部署脚本之Linux在线安装Redis一键安装Mysql部署脚本之Linux在线安装Mysql一键安装JAVA部署脚本之Linux在线安装JDK一键安装Nginx部署脚本之Linux在线安装NginxNavicat最新版(17)详细安装教程Xshell客户端免费版无需注册XFtp客户端免费版无需注册 前言…

JavaScript阻塞

JS对DOM树的阻塞 DOM的定义:文档对象模型,是JS操作网页的接口,指代页面中的元素。DOM树的定义:是指元素与元素之间的关系,可以指页面的结构。 JS在执行时会阻塞DOM树的结构,此时DOM树是不完整的&#xff0…

Mysql进阶(一)

1. 在ubuntu下安装MySQL数据库 1.1 查看操作系统版本 操作系统版本为Ubuntu22.04. LTS lsb_release -a; 安装成功之后,查看mysql的状态 1.2 查看mysql的状态 1.3 登录mysql mysql -uroot -p; 1.4 退出mysql quit; exit; 2. mysql 程序的…

安卓基础组件Looper - 03 java层面的剖析

文章目录 workflow工作线程 准备Looper创建LooperActivity主线程其他情况 Looper.prepare()大体流程java申请Loopernew LooperMessageQueue初始化 nativejniNativeMessageQueue Looper.loop()大体流程java获取Looper获取msg,处理msgLooper.loop()Looper.loopOnce &a…

DataStructsRECITE

1、绪论 什么是数据结构? 数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 数据结构包括三个方面:逻辑结构、存储结构、数据的运算。 逻辑结构有: 集合(数据元素除属于“同一个集合”外,别无其他关系…

自然语言处理:朴素贝叶斯

介绍 大家好,博主又来和大家分享自然语言处理领域的知识了。按照博主的分享规划,本次分享的核心主题本应是自然语言处理中的文本分类。然而,在对分享内容进行细致梳理时,我察觉到其中包含几个至关重要的知识点,即朴素…

【542. 01 矩阵 中等】

题目: 给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 示例 1: 输入:mat [[0,0,0],[0,1,0],[0,0,0]] 输出…

深入浅出:Spring AI 集成 DeepSeek 构建智能应用

Spring AI 作为 Java 生态中备受瞩目的 AI 应用开发框架,凭借其简洁的 API 设计和强大的功能,为开发者提供了构建智能应用的强大工具。与此同时,DeepSeek 作为领先的 AI 模型服务提供商,在自然语言处理、计算机视觉等领域展现了卓…

Vue 系列之:基础知识

什么是 MVVM MVVM(Model-View-ViewModel)一种软件设计模式,旨在将应用程序的数据模型(Model)与视图层(View)分离,并通过 ViewModel 来实现它们之间的通信。降低了代码的耦合度。 M…

辛格迪客户案例 | 鼎康生物电子合约系统(eSign)项目

01 案例企业 鼎康(武汉)生物医药有限公司于2013年06月19日成立 ,是一家总部位于湖北武汉的CDMO公司,坚持以客户为中心,以及时、经济和高质量为服务导向。鼎康生物拥有先进的150,000平方英尺的生产厂房,生产设施位于中国武汉的Bio…

QT-对象树

思维导图 写1个Widget窗口&#xff0c;窗口里面放1个按钮&#xff0c;按钮随便叫什么 创建2个Widget对象 Widget w1,w2 w1.show() w2不管 要求&#xff1a;点击 w1.btn ,w1隐藏&#xff0c;w2显示 点击 w2.btn ,w2隐藏&#xff0c;w1 显示 #include <QApplication> #inc…

【笔记】用大预言模型构建专家系统

最近闲庭漫步&#xff0c;赏一赏各个AI大语言模型芳容。也趁着时间&#xff0c;把倪海夏一家的天纪和人纪视频看完了&#xff0c;感谢倪先生和现在网络的知识分享&#xff0c;受益匪浅。但是发现看完&#xff0c;很多不错的知识都不能记录在脑子里&#xff0c;那用的时候岂不是…