📑往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)

✒️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

✒️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

✒️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

✒️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

✒️ 记录一场鸿蒙开发岗位面试经历~

✒️ 持续更新中……


长视频应用全屏播放页为用户带来沉浸式观看视频体验并支持选集。观察全屏播放页在不同设备上的UX设计图,可以进行如下设计:

  • 将全屏播放页划分为3个区域,如
示意图 sm md lg
全屏播放页
全屏播放选集页
  • 整个页面响应式适配,借助栅格组件能力监听不同断点变化实现不同的布局效果。
  • 全屏播放页区域1、2占满屏幕,点击选集弹出区域3,区域2中容器组件尺寸变化时,增大或减小的空间全部分配给进度条和下方工具栏的空白区域。
  • 全屏播放页区域3在不同断点下显示位置和大小不同。在sm断点下显示在右侧,视频与侧边栏比例为3:2;在md断点下显示在底部,视频与底部栏比例为1:1;在lg断点下显示在右侧,侧边栏宽度为固定值360vp。

长视频应用全屏播放页的3个基础区域介绍及实现方案如下表所示:

区域编号 简介 实现方案
1 全屏视频播放 使用 window窗口 管理窗口隐藏状态栏和导航栏,在sm断点下控制窗口横屏显示,在md、lg上不改变窗口显示方向。视频播放使用 AVPlayer 和 XComponent 等相关组件,不详细介绍具体功能实现,读者可以自行查看代码。
2 进度条及工具栏 进度条使用layoutWeight、工具栏的空白区域使用Blank实现自适应布局的拉伸能力,同 首页搜索框。
3 选集列表 选集列表通过 List组件 实现延伸能力,同 搜索页搜索发现 。

在实际开发中,区域1-2和区域3为并列的视频播放页内容,所以对应的开发顺序为区域1-2,区域3。另外,为了提升用户的使用体验,全屏播放页为折叠屏的半折叠态设计了视频悬停播放。

  • 全屏视频播放

进入全屏播放页,使用setWindowSystemBarEnable()方法隐藏顶部导航栏和状态栏,并在手机和折叠屏折叠态下使用setMainWindowOrientation()方法设置窗口横屏播放。

// features/VideoPlayer/src/main/ets/view/VideoPlayer.ets
aboutToAppear() {
  this.windowUtil = WindowUtil.getInstance();
  if (this.windowUtil !== undefined) {
    if (deviceInfo.deviceType !== CommonConstants.DEVICE_TYPES[0]) {
      // 隐藏顶部导航栏和状态栏
      this.windowUtil.disableWindowSystemBar();
    }
    if ((!display.isFoldable() && deviceInfo.deviceType === CommonConstants.DEVICE_TYPES[2]) ||
      display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_FOLDED) {
      // 设置窗口横屏播放
      this.windowUtil.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);
    }
    ...
  } else {
    Logger.info(`Full-screen display in portrait mode`);
  }
}
  • 视频悬停播放

在折叠屏上浏览视频详情页或全屏播放页时,折叠设备,将自动切换至悬停态的沉浸播放视频体验。折叠屏悬停适配通过 window 的setPreferredOrientation接口设置窗口横向显示,通过 display 的getCurrentFoldCreaseRegion接口获取折叠屏折痕区域的位置和大小,通过自定义方式实现悬停态页面。视频移到上半屏,中间为折叠屏避让区,其他可操作组件堆叠到下半屏。示意图如下:

md
// commons/base/src/main/ets/utils/DisplayUtil.ets
export class DisplayUtil {
  static getFCreaseRegion(): void {
    if (display.isFoldable()) {
      let foldRegion: display.FoldCreaseRegion = display.getCurrentFoldCreaseRegion();
      let rect: display.Rect = foldRegion.creaseRects[0];
      // 折叠屏避让区距离顶部高度,避让区本身高度
      let creaseRegion: number[] = [px2vp(rect.top), px2vp(rect.height)];
      AppStorage.setOrCreate('creaseRegion', creaseRegion);
    }
  }
}

// features/videoPlayer/src/main/ets/view/VideoPlayer.ets
aboutToAppear() {
  ...
  if (display.isFoldable()) {
    // 折叠屏状态为半折叠时,设置窗口横屏显示
    if (this.isHalfFolded) {
      this.windowUtil.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);
    }
  }
}

Column() {
  Stack({ alignContent: Alignment.Center }) {
    Flex({
      ...
    }) {
      Column() {
        ...
      }
      // 悬停时视频高度为避让区距离顶部高度
      .height(this.isHalfFolded ? this.creaseRegion[0] : CommonConstants.FULL_PERCENT)
    }

    Column() {
      ...
    }
    .height(CommonConstants.FULL_PERCENT)
    .width(CommonConstants.FULL_PERCENT)
    // 主轴尾部对齐,可操作组件堆叠到下半屏
    .justifyContent(FlexAlign.End)
    .visibility(this.isShowingSideBar ? Visibility.None : Visibility.Visible)

    Image($r('app.media.ic_public_back'))
      // 返回图标y轴的位置为避让区距离顶部高度+避让区本身的高度
      .position({
        x: $r('app.float.back_position_x'),
        y: this.isHalfFolded ? this.creaseRegion[0] + this.creaseRegion[1] : $r('app.float.back_position_y')
      })
  }
}

交互开发

交互事件归一

不同类型的智能设备,系统已经针对不同的交互方式做了适配,实现了 交互归一 ,因此开发者无需额外关注用户不同的交互方式。长视频应用包含的基础输入方式如下(以鼠标为例):

事件 应用支持的鼠标交互
点击 在Tabs、TextInput等组件上点击左键。
长按 在首页首个推荐视频上长按左键预览视频。
拖拽 在视频详情页右侧边栏上按压左键并移动鼠标,改变侧边栏的宽度。
滚动/平移 在List和Scroll组件中上下滚动滚轮,实现。
缩放 在首页推荐视频区域中ctrl+滚动滚轮控制宫格布局的缩放。

键盘走焦

在2in1设备的“华为发布会”的搜索结果页面,默认可获焦的组件 遵循走焦规范。点击搜索框进入输入场景时,激活并获焦到输入框。按Tab键,焦点从搜索框向下移动到栅格布局时,默认获焦第一张卡片的播放按钮,再次按Tab键焦点此时可向右移动,到尽头后焦点向上移动至搜索框。同时方向上下键控制焦点在卡片间移动,方向左右键控制焦点在卡片内部和侧边页签间移动。

资源使用

本节以长视频应用为例,根据实际的资源使用情况,给出“一多”应用开发中推荐的资源使用方式。

在长视频应用中,多端页面显示的效果不同,使用的资源值随断点变化,推荐使用 媒体查询 获取资源值。借助媒体查询中监听断点的功能做简单的封装,创建BreakpointType类,通过getValue方法获取对应的资源值。在使用时,创建不同的资源文件传入BreakpointType代表sm、md和lg断点下的资源值,实现应用窗口大小变化时的不同效果。

// commons/base/src/main/ets/utils/BreakpointType.ets
export class BreakpointType<T> {
  sm: T;
  md: T;
  lg: T;

  constructor(sm: T, md: T, lg: T) {
    this.sm = sm;
    this.md = md;
    this.lg = lg;
  }

  getValue(currentBreakpoint: string): T {
    if (currentBreakpoint === BreakpointConstants.BREAKPOINT_MD) {
      return this.md;
    }
    if (currentBreakpoint === BreakpointConstants.BREAKPOINT_LG) {
      return this.lg;
    } else {
      return this.sm;
    }
  }
}

// features/home/src/main/ets/view/DailyVideo.ets
Image($r('app.media.daily_small_video'))
  .height(new BreakpointType($r('app.float.small_daily_image_height_sm'),
    $r('app.float.small_daily_image_height_md'), $r('app.float.small_daily_image_height_lg'))
    .getValue(this.currentBreakpoint))
  .width(new BreakpointType($r('app.float.small_daily_image_width_sm'),
    $r('app.float.small_daily_image_width_md'), $r('app.float.small_daily_image_width_lg'))
    .getValue(this.currentBreakpoint))

功能开发

本示例的目标运行设备手机、折叠屏和2in1运行的都是标准系统,其系统能力一致,所以无需考虑 多设备上应用功能开发 的差异。

总结

本文针对长视频应用沉浸式的核心场景,从UX设计、工程管理、页面开发和功能开发四个角度,结合“一多”各项具体的能力,给出了在开发中推荐的参考样例。同时对于开发者在长视频垂类应用的“一多”开发中经常遇到痛点难点问题,给出详细的解决方案,实现了同类别应用的最佳实践,帮助开发者快速适配多种类型的设备,减少了多端开发的工作量,高效构建应用。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐