当前位置:   article > 正文

案例:新闻数据加载

案例:新闻数据加载

介绍

本篇Codelab是基于ArkTS的声明式开发范式实现的样例,主要介绍了数据请求和touch事件的使用。包含以下功能:

  1. 数据请求。
  2. 列表下拉刷新。
  3. 列表上拉加载。

相关概念

  • List组件:列表包含一系列相同宽度的列表项。
  • Tabs:通过页签进行内容视图切换。
  • TabContent:仅在Tabs中使用,对应一个切换页签的内容视图。
  • 数据请求:提供HTTP数据请求能力。
  • 触摸事件onTouch:触摸动作触发调用该方法。

相关权限

网络数据请求需要申请权限:ohos.permission.INTERNET。

约束与限制

本篇Codelab需要搭建服务端环境,服务端如何搭建将在代码工程目录的README中详细介绍,文档中不再赘述。

完整示例

gitee源码地址

源码下载

新闻数据加载(ArkTS).zip

环境搭建

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。

软件要求

硬件要求

  • 设备类型:华为手机或运行在DevEco Studio上的华为手机设备模拟器。
  • HarmonyOS系统:3.1.0 Developer Release。

环境搭建

  1. 安装DevEco Studio,详情请参考下载和安装软件
  2. 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境: 
    • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
    • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境
  3. 开发者可以参考以下链接,完成设备调试的相关配置: 

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在源码下载或gitee中提供。

  1. ├──entry/src/main/ets // ArkTS代码区
  2. │ ├──common
  3. │ │ ├──constant
  4. │ │ │ └──CommonConstant.ets // 公共常量类
  5. │ │ └──utils
  6. │ │ ├──HttpUtil.ets // 网络请求方法
  7. │ │ ├──Logger.ets // 日志工具类
  8. │ │ ├──PullDownRefresh.ets // 下拉刷新方法
  9. │ │ └──PullUpLoadMore.ets // 上拉加载更多方法
  10. │ ├──entryability
  11. │ │ └──EntryAbility.ts // 程序入口类
  12. │ ├──pages
  13. │ │ └──Index.ets // 主页面
  14. │ ├──view
  15. │ │ ├──CustomRefreshLoadLayout.ets // 下拉刷新、上拉加载布局文件
  16. │ │ ├──LoadMoreLayout.ets // 上拉加载布局封装
  17. │ │ ├──NewsItem.ets // 新闻数据
  18. │ │ ├──NewsList.ets // 新闻列表
  19. │ │ ├──NoMoreLayout.ets // 上拉停止布局封装
  20. │ │ ├──RefreshLayout.ets // 下拉刷新布局封装
  21. │ │ └──TabBar.ets // 新闻类型页签
  22. │ └──viewmodel
  23. │ ├──NewsModel.ets // 新闻模型类
  24. │ └──NewsViewModel.ets // 新闻ViewModel
  25. ├──entry/src/main/resources // 资源文件目录
  26. └──HttpServerOfNews // 服务端代码

构建主界面

本章节将介绍新闻列表页面的实现,用tabBar展示新闻分类,tabContent展示新闻列表,效果图如图所示:

在TabBar.ets文件中的aboutToAppear()方法里获取新闻分类。

  1. // TabBar.ets
  2. aboutToAppear() {
  3. // 请求服务端新闻类别
  4. NewsViewModel.getNewsTypeList().then((typeList: NewsTypeBean[]) => {
  5. this.tabBarArray = typeList;
  6. }).catch((typeList: NewsTypeBean[]) => {
  7. this.tabBarArray = typeList;
  8. });
  9. }

在NewsList.ets文件中的aboutToAppear()方法里获取新闻数据,将数据加载到新闻列表页面ListLayout布局中。

  1. // NewsList.ets
  2. changeCategory() {
  3. this.newsModel.currentPage = 1;
  4. NewsViewModel.getNewsList(this.newsModel.currentPage, this.newsModel.pageSize, Const.GET_NEWS_LIST)
  5. .then((data: NewsData[]) => {
  6. this.newsModel.pageState = PageState.Success;
  7. if (data.length === this.newsModel.pageSize) {
  8. this.newsModel.currentPage++;
  9. this.newsModel.hasMore = true;
  10. } else {
  11. this.newsModel.hasMore = false;
  12. }
  13. this.newsModel.newsData = data;
  14. })
  15. .catch((err: string | Resource) => {
  16. promptAction.showToast({
  17. message: err,
  18. duration: Const.ANIMATION_DURATION
  19. });
  20. this.newsModel.pageState = PageState.Fail;
  21. });
  22. }
  23. aboutToAppear() {
  24. // 请求服务端新闻数据
  25. this.changeCategory();
  26. }
  27. ...
  28. @Builder ListLayout() {
  29. List() {
  30. ...
  31. ForEach(this.newsModel.newsData, (item: NewsData) => {
  32. ListItem() {
  33. // 新闻数据
  34. NewsItem({ newsData: item })
  35. }
  36. .height($r('app.float.news_list_height'))
  37. .backgroundColor($r('app.color.white'))
  38. .margin({ top: $r('app.float.news_list_margin_top') })
  39. .borderRadius(Const.NewsListConstant_ITEM_BORDER_RADIUS)
  40. }, (item: NewsData, index?: number) => JSON.stringify(item) + index)
  41. ...
  42. }
  43. ...
  44. }

数据请求

在module.json5文件中配置如右侧所示权限:

这一章节,将基于新闻数据请求来介绍如何从服务端请求数据。

  1. // module.json5
  2. "requestPermissions": [
  3. {
  4. "name": "ohos.permission.INTERNET",
  5. "reason": "$string:dependency_reason",
  6. "usedScene": {
  7. "abilities": [
  8. "EntryAbility"
  9. ],
  10. "when": "inuse"
  11. }
  12. }
  13. ]

导入http模块,封装httpRequestGet方法,调用者传入url地址发起网络数据请求。

  1. // HttpUtil.ets
  2. import http from '@ohos.net.http';
  3. ...
  4. export function httpRequestGet(url: string): Promise<ResponseResult> {
  5. let httpRequest = http.createHttp();
  6. // 发送数据请求
  7. let responseResult = httpRequest.request(url, {
  8. method: http.RequestMethod.GET,
  9. readTimeout: Const.HTTP_READ_TIMEOUT,
  10. header: {
  11. 'Content-Type': ContentType.JSON
  12. },
  13. connectTimeout: Const.HTTP_READ_TIMEOUT,
  14. extraData: {}
  15. });
  16. let serverData: ResponseResult = new ResponseResult();
  17. // 处理数据,并返回
  18. return responseResult.then((value: http.HttpResponse) => {
  19. Logger.info(`http value ${JSON.stringify(value)}`);
  20. if (value.responseCode === Const.HTTP_CODE_200) {
  21. // 获取返回数据
  22. let result = `${value.result}`;
  23. let resultJson: ResponseResult = JSON.parse(result);
  24. if (resultJson.code === Const.SERVER_CODE_SUCCESS) {
  25. serverData.data = resultJson.data;
  26. }
  27. serverData.code = resultJson.code;
  28. serverData.msg = resultJson.msg;
  29. } else {
  30. serverData.msg = `${$r('app.string.http_error_message')}&${value.responseCode}`;
  31. }
  32. return serverData;
  33. }).catch(() => {
  34. serverData.msg = $r('app.string.http_error_message');
  35. return serverData;
  36. })
  37. }

在NewsViewModel.ets文件中封装getNewsList方法,调用httpRequestGet方法请求服务端,用Promise异步保存返回的新闻数据列表。

  1. // NewsViewModel.ets
  2. // 获取服务端新闻数据列表
  3. getNewsList(currentPage: number, pageSize: number, path: string): Promise<NewsData[]> {
  4. return new Promise(async (resolve: Function, reject: Function) => {
  5. let url = `${Const.SERVER}/${path}`;
  6. url += '?currentPage=' + currentPage + '&pageSize=' + pageSize;
  7. httpRequestGet(url).then((data: ResponseResult) => {
  8. if (data.code === Const.SERVER_CODE_SUCCESS) {
  9. resolve(data.data);
  10. } else {
  11. Logger.error('getNewsList failed', JSON.stringify(data));
  12. reject($r('app.string.page_none_msg'));
  13. }
  14. }).catch((err: Error) => {
  15. Logger.error('getNewsList failed', JSON.stringify(err));
  16. reject($r('app.string.http_error_message'));
  17. });
  18. });
  19. }

下拉刷新

本章节将以下拉刷新的功能效果来介绍touch事件的使用。效果图如图所示:

创建一个下拉刷新布局CustomLayout,动态传入刷新图片和刷新文字描述。

  1. // CustomRefreshLoadLayout.ets
  2. build() {
  3. Row() {
  4. // 下拉刷新图片
  5. Image(this.customRefreshLoadClass.imageSrc)
  6. ...
  7. // 下拉刷新文字
  8. Text(this.customRefreshLoadClass.textValue)
  9. ...
  10. }
  11. ...
  12. }

将下拉刷新的布局添加到NewsList.ets文件中新闻列表布局ListLayout里面,监听ListLayout组件的onTouch事件实现下拉刷新。

  1. // NewsList.ets
  2. build() {
  3. Column() {
  4. if (this.newsModel.pageState === PageState.Success) {
  5. this.ListLayout()
  6. }
  7. ...
  8. }
  9. ...
  10. .onTouch((event: TouchEvent | undefined) => {
  11. if (event) {
  12. if (this.newsModel.pageState === PageState.Success) {
  13. listTouchEvent(this.newsModel, event);
  14. }
  15. }
  16. })
  17. }
  18. ...
  19. @Builder ListLayout() {
  20. List() {
  21. ListItem() {
  22. RefreshLayout({
  23. refreshLayoutClass: new CustomRefreshLoadLayoutClass(this.newsModel.isVisiblePullDown, this.newsModel.pullDownRefreshImage,
  24. this.newsModel.pullDownRefreshText, this.newsModel.pullDownRefreshHeight)
  25. })
  26. ...
  27. }
  28. }
  29. ...
  30. }
  1. 在onTouch事件中,listTouchEvent方法判断触摸事件是否满足下拉条件。如右侧listTouchEvent所示:
  2. 在touchMovePullRefresh方法中,我们将对下拉的偏移量与下拉刷新布局的高度进行对比,如果大于布局高度并且在新闻列表的顶部,则表示达到刷新条件。如右侧touchMovePullRefresh所示:
  3. 在pullRefreshState方法中我们会对下拉刷新布局中的状态图片和描述进行改变,如右侧pullRefreshState所示。
  4. 当手指松开,才执行刷新操作。

上拉加载也是通过touch事件来实现的,此处不再赘叙,有兴趣的同学可参考代码。

  1. // PullDownRefresh.ets
  2. export function listTouchEvent(newsModel: NewsModel, event: TouchEvent) {
  3. switch (event.type) {
  4. ...
  5. case TouchType.Move:
  6. if ((newsModel.isRefreshing === true) || (newsModel.isLoading === true)) {
  7. return;
  8. }
  9. let isDownPull = event.touches[0].y - newsModel.lastMoveY > 0;
  10. if (((isDownPull === true) || (newsModel.isPullRefreshOperation === true)) && (newsModel.isCanLoadMore === false))
  11. {
  12. // 手指移动,处理下拉刷新
  13. touchMovePullRefresh(newsModel, event);
  14. }
  15. ...
  16. break;
  17. }
  18. }
  19. export function touchMovePullRefresh(newsModel: NewsModel, event: TouchEvent) {
  20. if (newsModel.startIndex === 0) {
  21. newsModel.isPullRefreshOperation = true;
  22. let height = vp2px(newsModel.pullDownRefreshHeight);
  23. newsModel.offsetY = event.touches[0].y - newsModel.downY;
  24. // 滑动偏移量大于下拉刷新布局高度,满足刷新条件。
  25. if (newsModel.offsetY >= height) {
  26. pullRefreshState(newsModel, RefreshState.Release);
  27. newsModel.offsetY = height + newsModel.offsetY * Const.Y_OFF_SET_COEFFICIENT;
  28. } else {
  29. pullRefreshState(newsModel, RefreshState.DropDown);
  30. }
  31. if (newsModel.offsetY < 0) {
  32. newsModel.offsetY = 0;
  33. newsModel.isPullRefreshOperation = false;
  34. }
  35. }
  36. }
  37. export function pullRefreshState(newsModel: NewsModel, state: number) {
  38. switch (state) {
  39. ...
  40. case RefreshState.Release:
  41. newsModel.pullDownRefreshText = $r('app.string.release_refresh_text');
  42. newsModel.pullDownRefreshImage = $r('app.media.ic_pull_up_refresh');
  43. newsModel.isCanRefresh = true;
  44. newsModel.isRefreshing = false;
  45. break;
  46. case RefreshState.Refreshing:
  47. newsModel.offsetY = vp2px(newsModel.pullDownRefreshHeight);
  48. newsModel.pullDownRefreshText = $r('app.string.refreshing_text');
  49. newsModel.pullDownRefreshImage = $r('app.media.ic_pull_up_load');
  50. newsModel.isCanRefresh = true;
  51. newsModel.isRefreshing = true;
  52. break;
  53. case RefreshState.Success:
  54. newsModel.pullDownRefreshText = $r('app.string.refresh_success_text');
  55. newsModel.pullDownRefreshImage = $r('app.media.ic_succeed_refresh');
  56. newsModel.isCanRefresh = true;
  57. newsModel.isRefreshing = true;
  58. break;
  59. ...
  60. default:
  61. break;
  62. }
  63. }

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 使用List组件实现数据列表。

  2. 使用Tabs、TabContent组件实现内容视图切换。

  3. 网络数据请求。

  4. 触摸事件onTouch的使用。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/244617
推荐阅读
相关标签
  

闽ICP备14008679号