鸿蒙NEXT开发【新闻app首页信息流】业务场景与办理方案 ...

打印 上一主题 下一主题

主题 996|帖子 996|积分 2988

简介

本场景办理方案主要面向于新闻类页面开发职员,指导开发者从零开始构建一个新闻类的首页面,包含地址选择、tabs和tabContent切换的动态图标和流畅动效、下拉革新上拉加载、首页feed流等常见功能,及功能的流畅体验。
整体场景介绍

主要介绍了用户操作应用的主要流程,展示了用户进入首页通过页签切换页面内容,上拉加载和下拉革新页面,从首页地址进入地址选择页更换地址等功能结果。


  • 实现应用的主要流程图

 


  • 运行结果图
 


  • 操作流程 1. 获取地理位置的权限;
    2. 点击位置信息,跳转地址页,可修改当前位置信息;
    3. 点击顶部页签大概滑动切换页面,页签同步切换;
    4. 点击底部页签切换页面,同步切换页签,触发页签切换的动画结果;
    5. 下拉革新页面信息;
    6. 上拉加载页面信息;
    7. 点击右下角按钮回弹至顶部。
场景分析

适用范围

本场景主要适用于新闻类的应用首页,使用原生组件及三方库组件完成新闻首页与功能的实现。
场景上风

本场景可以带给用户更加流畅和便捷的首页体验。具体上风如下:

  • 导航栏点击切换动效流畅,相应时延为51ms。
  • 左右滑动切换动效流畅,相应时延为67ms。
  • 地址选择页定位精确,选择目标都会更加便捷。
  • 底部页签跳转风雅流畅不丢帧,时延349ms。
  • 具有上拉加载下拉革新页面功能,动效回弹流畅不丢帧,下拉革新相应时延为153ms,上拉加载相应时延为150ms。
场景分析

典范场景与实现方案



  • 实现方案如下表:    场景名称
      描述
      实现方案
      导航栏切换动效流畅
      点击页签大概滑动切换页面,页签同步切换
      tab组件添加动画开始时触发事件
      底部页签跳转风雅流畅
      底部页签切换具有动画结果
      添加lottie动画
      上拉加载下拉革新
      上拉加载更多的新闻内容,下拉革新整个页面,均具有加载动效
      pullToRefresh组件
      首页feed流
      首页展示流畅图文列表
      使用LazyForEach对子组件举行渲染,实现懒加载功能
      地址选择页
      提供地址选择,定位,地址首字母定位及模糊查询功能
      位置服务与AlphabetIndexer组件

场景实现

导航栏切换动效流畅

通过添加tab组件动效触发事件实现页面内容切换与页签样式切换同步触发。结果如图所示:




  • 动效触发事件节点 保举使用onAnimationStart事件设置切换标签动效,使用onChange会导致页面切换后再触发动效导致结果耽误触发,使用onClick事件会与页面切换冲突。
    1. build() {
    2. Tabs({ barPosition: BarPosition.Start }) {
    3. ForEach(this.tabBarArray, (tabsItem: NewsTypeModel, index: number) => {
    4. TabContent() {
    5. ...
    6. }
    7. ...
    8. }, (item: NewsTypeModel) => JSON.stringify(item));
    9. }
    10. ...
    11. .onAnimationStart((_index: number, targetIndex: number, _event: TabsAnimationEvent) => {
    12. this.currentIndex = targetIndex;
    13. })
    14. }
    15. // 标签样式
    16. @Builder
    17. TabBuilder(id: number, index: number) {
    18. Column() {
    19. Text(this.tabBarArray[id].name)
    20. ...
    21. }
    22. .alignItems(HorizontalAlign.Start)
    23. }
    复制代码

底部页签跳转风雅流畅

底部页签样式添加lottie动画使跳转风雅流畅。结果如图所示:




  • 底部页签跳转功能时序图



  • TabBar集成lottie动画 Lottie是一个适用于OpenHarmony的动画库,它可以解析Adobe After Effects软件通过Bodymovin插件导出的json格式的动画,并在移动设备上举行本地渲染。支持动画的交互性,通过添加触摸事件与TabBar相结合可实现动态图标结果。
    引入lottie三方库。
    准备lottie动画资源,发起放置到Entry目录下的common文件夹下(放置本模块中,使用相对路径无法读取)。
    导入lottie模块。
    1. import lottie from "@ohos/lottie";
    复制代码
    使用RenderingContext在Canvas组件上举行绘制,声明CanvasRenderingContext2D变量,RenderingContextSettings用来配置CanvasRenderingContext2D对象的参数表明canvas是否开启抗锯齿。
    1. private renderingSettings1: RenderingContextSettings = new RenderingContextSettings(true);
    2. private canvasRenderingContext1: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderingSettings1);
    复制代码
    定义所需数据范例的接口,初始化变量,接口中需要包含资源路径信息和CanvasRenderingContext2D。
    1. interface TabBarOption {
    2. index: number;
    3. text: string;
    4. name: string;
    5. path: string;
    6. canvasRenderingContext: CanvasRenderingContext2D;
    7. }
    复制代码
    实现动画播放方法。
    1. lottieController(): void {
    2. if (this.currentBottomIndex === 0) {
    3. lottie.stop();
    4. lottie.play(this.tabBarOption1.name);
    5. }
    6. if (this.currentBottomIndex === 1) {
    7. lottie.stop();
    8. lottie.play(this.tabBarOption2.name);
    9. }
    10. if (this.currentBottomIndex === 2) {
    11. lottie.stop();
    12. lottie.play(this.tabBarOption3.name);
    13. }
    14. }
    复制代码
    在TabBar样式中实现子组件Canvas。
    1. Canvas(tabBarOption.canvasRenderingContext)
    2. .width($r('app.float.canvas_size'))
    3. .height($r('app.float.canvas_size'))
    4. .onReady(() => {
    5. lottie.loadAnimation({
    6. container: tabBarOption.canvasRenderingContext,
    7. renderer: 'canvas',
    8. loop: false,
    9. autoplay: false,
    10. name: tabBarOption.name,
    11. path: tabBarOption.path,
    12. });
    13. this.lottieController();
    14. })
    复制代码
    在tab组件的onAnimationStart事件中调用播放方法。
    1. .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
    2. this.currentBottomIndex = targetIndex
    3. this.lottieController();
    4. })
    复制代码

上拉加载下拉革新

通过三方库组件pullToRefresh实现下拉革新页面,上拉加载更多数据结果。结果如图所示:




  • 上拉加载下拉革新功能时序图



  • pullToRefresh组件 使用第三方库pullToRefresh组件,将列表组件、绑定的数据对象和scroller对象包含进去,并添加上滑与下拉方法。支持lazyForEarch的数据作为数据源,使用的List组件需要设置edgeEffect属性为(EdgeEffect.None)。
    引入三方库pullToRefresh。
    1. ohpm install @ohos/pulltorefresh
    复制代码
    导入lottie模块。
    1. import { PullToRefresh } from '@ohos/pulltorefresh/index';
    复制代码
    准备数据源,本场景使用本地资源模拟结果,资源文件在resources/rawfile目录下,实际情况可替换为从网络获取。
    1. const MOCK_DATA_FILE_ONE_DIR: string = 'mockDataOne.json';
    2. const MOCK_DATA_FILE_TWO_DIR: string = 'mockDataTwo.json';
    复制代码
    将列表组件、绑定的数据对象和scroller对象包含进去,并添加上滑与下拉方法。
    1. PullToRefresh({
    2. // 必传项,列表组件所绑定的数据
    3. data: $newsData,
    4. // 必传项,需绑定传入主体布局内的列表或宫格组件
    5. scroller: this.scroller,
    6. // 必传项,自定义主体布局,内部有列表或宫格组件
    7. customList: () => {
    8. // 一个用@Builder修饰过的UI方法
    9. this.getListView();
    10. },
    11. // 下拉刷新回调
    12. onRefresh: () => {
    13. return new Promise<string>((resolve, reject) => {
    14. ...
    15. });
    16. },
    17. // 上滑加载回调
    18. onLoadMore: () => {
    19. return new Promise<string>((resolve, reject) => {
    20. ...
    21. });
    22. },
    23. customLoad: null,
    24. customRefresh: null,
    25. })
    复制代码

首页feed流

通过懒加载实现首页feed流快速渲染与流畅滑动。结果如图所示:




  • 懒加载 新闻应用列表数据往往会到达上万条,针对这类大量数据加载的长列表应用,使用懒加载办理一次性加载长列表数据耗时长、占用过多资源的问题,可以提拔页面相应速度。
    创建需要加载的数据源。
    1. @State newsData: NewsDataSource = new NewsDataSource();
    复制代码
    创建子组件。
    1. @Component
    2. struct newsItem{...}
    复制代码
    使用LazyForEach对子组件举行渲染。
    1. List({ space: 3, scroller: this.scroller }) {
    2. LazyForEach(this.newsData, (item: NewsData) => {
    3. ListItem() {
    4. newsItem({...})
    5. ...
    6. }
    7. }, (item: NewsData, index?: number) => JSON.stringify(item) + index);
    8. }
    复制代码

地址选择页

使用原生位置服务实现定位功能,AlphabetIndexer组件实现地址首字母定位导航条。结果如图所示:




  • 地址选择页结果功能时序图



  • 位置服务与索引条导航 开启手机位置服务功能,使用位置服务提供GNSS定位、网络定位、逆地理编码的功能获取当前地理位置信息,通过AlphabetIndexer组件实现首字母快速定位都会的索引条导航。
    申请权限,API9及之后的版本,需要申请ohos.permission.APPROXIMATELY_LOCATION大概同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION;无法单独申请ohos.permission.LOCATION。
    1. requestPermissions: [
    2. {
    3. "name": "ohos.permission.APPROXIMATELY_LOCATION",
    4. "reason" : "$string:reason",
    5. "usedScene" : {}
    6. },
    7. {
    8. "name": "ohos.permission.LOCATION",
    9. "reason" : "$string:reason",
    10. "usedScene" : {}
    11. }
    12. ]
    复制代码
    申请用户安全权限。
    1. onPageShow(): void {
    2. abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(), [
    3. 'ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']).then(() => {
    4. ...
    5. });
    6. }
    复制代码
    导入位置服务。
    1. import { geoLocationManager } from '@kit.LocationKit';
    复制代码
    获取地理位置信息,举行逆地理编码获取当前位置。
    1. ...
    2. let locationChange: (err: BusinessError, location: geoLocationManager.Location) => void = (err, location) => {
    3. if (err) {
    4. hilog.error(0x00000, 'locationChanger: err=', JSON.stringify(err));
    5. }
    6. if (location) {
    7. let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
    8. 'latitude': location.latitude,
    9. 'longitude': location.longitude,
    10. 'maxItems': CommonConstants.MAX_ITEMS
    11. };
    12. geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, (err, data) => {
    13. if (data) {
    14. hilog.info(0x00000, 'getAddressesFromLocation: data=', JSON.stringify(data));
    15. if (data[0].locality !== undefined) {
    16. AppStorage.setOrCreate('local', data[0].locality.replace(/"/g, '').slice(0, -1));
    17. AppStorage.setOrCreate('currentLocal', data[0].locality.replace(/"/g, '').slice(0, -1));
    18. }
    19. }
    20. });
    21. }
    22. };
    23. ...
    24. onPageShow(): void {
    25. abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(), [
    26. 'ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']).then(() => {
    27. if (this.status) {
    28. geoLocationManager.getCurrentLocation(locationChange);
    29. this.status = false;
    30. }
    31. });
    32. }
    33. ...
    复制代码
    使用AlphabetIndexer组件实现导航条。
    1. ...
    2. AlphabetIndexer({arrayValue: TAB_VALUE, selected: this.stabIndex })
    3. .height(CommonConstants.FULL_PERCENT)
    4. .selectedColor($r('app.color.alphabet_select_color'))
    5. .popupColor($r('app.color.alphabet_pop_color'))
    6. .selectedBackgroundColor($r('app.color.alphabet_selected_bgc'))
    7. .popupBackground($r('app.color.alphabet_pop_bgc'))
    8. .popupPosition({ x: $r('app.integer.pop_position_x'), y: $r('app.integer.pop_position_y') })
    9. .usingPopup(true)
    10. .selectedFont({size: $r('app.integer.select_font'), weight: FontWeight.Bolder })
    11. .popupFont({ size: $r('app.integer.pop_font'), weight: FontWeight.Bolder })
    12. .alignStyle(IndexerAlign.Right)
    13. ...
    复制代码
    通过AlphabetIndexer组件onSelect事件获取选中索引值,通过Scroller的scrollToIndex方法滑动到指定索引位置。
    1. ...
    2. private scroller: Scroller = new Scroller();
    3. ...
    4. AlphabetIndexer({ arrayValue: TAB_VALUE, selected: this.stabIndex })
    5. ...
    6. .onSelect((tabIndex: number) => {
    7. this.scroller.scrollToIndex(tabIndex);
    8. })
    9. ...
    复制代码


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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

笑看天下无敌手

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

标签云

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