头图

HarmonyOS实战IM系统之:一次开发多端部署

image.png

背景介绍

即时通讯功能已经成为现代应用中不可或缺的一部分,对提升用户体验、提高业务效率、增强客户服务、推动业务增长具有重要作用。从刚工作时做的秀场直播应用到现在的房屋平台应用,IM都在其中发挥了重要作用。随着鸿蒙系统的崛起及鸿蒙生态的成熟,很多厂商还是适配HarmonyOS Next系统,有幸成为先行者,希望从HarmonyOS的新特性探索IM场景的新方式。

本文分享IM中的一多落地实践。

1、什么是一次开发多端部署

我们先来看看一次开发多端部署(简称“一多”)官方的定义:
一套代码工程,一次开发上架,多端按需部署。

目标:支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。

为了实现“一多”的目标,需要解决两个基础问题:

  • 不同大小、颜色的设备如何适配;
  • 不同系统能力的设备,比如电视没有摄像头,手表没有麦克风,功能如何兼容。

一句话总结就是:“一多就是用一套代码解决不同设备的页面适配问题和功能适配问题**,本文我们主要分析页面适配问题。

了解了一多定义后我们看看之前不同设备间适配的痛点。

刚工作做的秀场直播,主要是两个平台Windows和Android,各自用原生实现,可以现象效率很低。后面Windows平台开发框架切换为QT,可以实现跨平台,QT一方面会引来包体积的增加,二是由于Windows与Android平台屏幕大小不一致,导致UI设计时两个平台的布局也完全不一样,所以只能做到业务逻辑跨平台,UI还是得各自开发;
现在流行的Flutter也是一样的问题,由于UI布局不同,所以UI侧必须适配不同屏幕大小的平台。

在HarmonyOS Next之前,要实现比较满意的UI效果,市面上完全没有一套代码的解决方案,必须要分别适配。HarmonyOS的页面级一多功能将彻底解决这个问题。

2、一多能为IM带来什么

在IM场景中,下面以飞书聊天功能为例,在PC中会话列表和聊天页面是左右布局:

image.png

在手机端屏幕变小,会话列表和聊天页是完全不同的页面。
image.png

image.png

我们可以基于HarmonyOS Next的页面级一多能力,使用一套代码实现上述聊天界面适配。

3、HarmonyOS一多介绍

我们思考一下,抛开HarmonyOS不说,如果让我们实现一个系统完成上述能力,需要怎么实现。

第一肯定是设备独立像素,就是在Android端用到的dp单位,可以随着设备的大小自适应内容大小;
第二需要支持不同大小屏幕设备切换使用不同UI。

HarmonyOS ArkUI中提供了两种布局能力解决上面的两个问题:

  1. 自适应布局:当外部容器大小发生变化时,页面元素可以根据相对关系自动变化以适应外部容器变化的布局能力,比如聊天页中,大屏会话列表和聊天页通知展示,小屏幕分开展示。相对关系比如有占比、固定宽高比、显示优先级等。ArkTS自适应布局能力有7种:拉伸能力、均分能力、占比能力、缩放能力、延伸能力、隐藏能力、折行能力。自适应布局能力可以实现界面显示随外部容器大小连续变化。
  2. 响应式布局: 当外部容器大小发生变化时,元素可以根据断点、栅格或特定的特征(如屏幕方向、窗口宽高等)自动变化以适应外部容器变化的布局能力。ArkTS响应式布局能力有3种:断点、媒体查询、栅格布局。响应式布局可以实现界面随外部容器大小不连续变化,通常不同特征下的界面显示会有较大的差异。
3.1 自适应布局

ArkUI提炼了七种自适应布局能力:

  1. 拉伸能力:容器组件尺寸发生变化时,增加或减小的空间全部分配给容器组件内指定区域。通过Flex布局的flexGrow和flexShrink属性来提供这种能力。
  2. 均分能力:容器组件尺寸发生变化时,增加或减小的空间均匀分配给容器组件内所有空白区域。通过给Row组件、Column组件或Flex组件的justifyContent属性设置为FlexAlign.SpaceEvenly实现这种能力,比如首页的TAB一般是均分。
  3. 占比能力:子组件的宽或高按照预设的比例,随容器组件发生变化。 基于通用属性的两种实现方式:将子组件的宽高设置为父组件宽高的百分比、layoutWeight属性
  4. 缩放功能:子组件的宽高按照预设的比例,随容器组件发生变化,且变化过程中子组件的宽高比不变。通过布局约束的aspectRatio属性实现。
  5. 延伸能力:容器组件内的子组件,按照其在列表中的先后顺序,随容器组件尺寸变化显示或隐藏。基于容器组件的两种实现方式:通过List组件实现、通过Scroll组件配合Row组件或Column组件实现
  6. 隐藏能力:容器组件内的子组件,按照其预设的显示优先级,随容器组件尺寸变化显示或隐藏。相同显示优先级的子组件同时显示或隐藏。通过布局约束的displayPriority属性实现。
  7. 折行能力:容器组件尺寸发生变化时,如果布局方向尺寸不足以显示完整内容,自动换行。通过Flex组件的wrap属性设置为FlexWrap.Wrap实现。
3.2 响应式布局

自适应布局可以保证窗口尺寸在一定范围内变化时,页面的显示是正常的。但是将窗口尺寸变化较大时(如折叠屏手机窗口宽度从400vp变化为1000vp),仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题(比如feed流,小屏幕一般显示两列,但是在大屏幕中显示两列太稀疏,要显示四列才满足美感),此时就需要借助响应式布局能力调整页面结构。

响应式布局是指页面内的元素可以根据特定的特征(如窗口宽度、屏幕方向等)自动变化以适应外部容器变化的布局能力。响应式布局中最常使用的特征是窗口宽度,可以将窗口宽度划分为不同的范围(AkrUI中称为断点)。当窗口宽度从一个断点变化到另一个断点时,改变页面布局(如将页面内容从单列排布调整为双列排布甚至三列排布等)以获得更好的显示效果。

当前系统提供了如下三种响应式布局能力:

  1. 断点:将窗口宽度划分为不同的范围(即断点),监听窗口尺寸变化,当断点改变时同步调整页面布局。
  2. 媒体查询:媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。
  3. 栅格布局:栅格组件将其所在的区域划分为有规律的多列,通过调整不同断点下的栅格组件的参数以及其子组件占据的列数等,实现不同的布局效果。

了解了一对的UI能力,下面我们看具体在IM中落地。

4、一多在IM系统中落地实战

4.1 适配屏幕大小变化

栅格布局
栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题,保证不同设备上各个模块的布局一致性。
比如以一般应用我的页面为例,小屏幕占满显示,对于大屏幕我们希望两边留白,这个时候可以通过栅格布局来解决。栅格布局通过参数来觉得屏幕分割成几列,然后不同屏幕可以设置不同的间隔等。
ArkUI提供了GridRow与GridCol来实现栅格化布局,这里基于栅格化布局实现断点监听。

示例代码:

build() {  
  GridRow({  
    columns: {  
      sm: 4,  
      md: 12,  
      lg: 12  
    }  
  }) {  
    GridCol({ span: {  
      sm: 4,  
      md: 12,  
      lg: 12  
    } }) {  
      Column() {  
        Home()  
      }  
      .width('100%')  
      .height('100%')  
  
    }  
    .height('100%')  
    .width('100%')  
  }  
  .onBreakpointChange((breakPoints) => {  
    this.currentBreakpoint = breakPoints;  
  })  
}
4.2 不同屏幕展示不同内容

Nativation组件
如何实现不同屏幕展示不同页面呢?会话列表页在手机上只显示列表,在折叠屏或者PC分割显示,左侧列表,右侧详情页?
ArkUI提供了Nativation组件。

Navigation组件一般作为页面的根容器,包括单页面、分栏和自适应三种显示模式。Navigation组件适用于模块内页面切换,一次开发,多端部署场景。通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。一多场景下,Navigation组件能够自动适配窗口显示大小,在窗口较大的场景下自动切换分栏展示效果。

这里主要用到两个属性:

  • navBarWidth:导航栏宽度,仅在Navigation组件分栏时生效
  • navDestination:用来创建NavDestination组件,根据string构建不同的内容页,比如在普通聊天可以builder聊天页,在没有选中聊天用户时builder空白页。
  • mode:导航栏的显示模式,默认是NavigationMode.Auto,我们在大屏使用NavigationMode.Split,在手机使用NavigationMode.Stack

示例代码:

build() {  
  Column() {  
    
      Navigation(this.pageInfo) {   
          Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center }) {  
            ConversationList({  
              currentConversationUserName: $currentConversationUserName,  
              currentContactUserName: $currentContactUserName  
            })  
              .flexGrow(BaseConstants.FLEX_GROW_ONE)  
              .width(BaseConstants.FULL_WIDTH)  
      }      
      .hideTitleBar(true)  
      .hideToolBar(true)  
      .navBarWidth(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? HomeConstants.NAVIGATION_NAV_BAR_WIDTH_LG :  
      HomeConstants.NAVIGATION_NAV_BAR_WIDTH_MD)  
      .navDestination(this.PageMap)  
      .mode(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? NavigationMode.Stack : NavigationMode.Split)  
      .width(BaseConstants.FULL_WIDTH)  
    }
}

@Builder  
PageMap(name: string) {  
  if (name === 'ConversationDetail') {  
    ConversationDetail({ currentConversationUserName: this.currentConversationUserName, currentFeatureIndex: 1 });  
  } else if (name === 'ConversationDetailNone') {  
    ConversationDetailNone();  
  } else {  
    ConversationDetailNone();  
  }  
}

5、总结

本文通过IM聊天功能的适配,介绍了鸿蒙页面级一多的特性和能力。ArkUI能力真正实现了一套代码多端部署,解决了跨平台的最后一公里。期待HarmonyOS生态的健全,尽早体验流畅便捷的全新产品。


轻口味
28.1k 声望4.5k 粉丝

移动端十年老人,主要做IM、音视频、AI方向,目前在做鸿蒙化适配,欢迎这些方向的同学交流:wodekouwei