当前位置:   article > 正文

鸿蒙App开发-网络请求-下拉刷新三方库-底部Tab栏-滚动组件(含源码)_鸿蒙app开发 harmonyos:网络请求+三

鸿蒙app开发 harmonyos:网络请求+三

本文介绍一个基于鸿蒙ArkTS开发的App,是一个包含轮播图、文章列表和 Web 页面等功能的多页面应用。

本文主要内容包括:

一、效果图

首页
详情页

  

二、内容简介

1.底部Tab栏和两个页面

        App底部是一个TabBar,点击TabBar可以切换上面的页面。共包含两个页面,一个是“首页” ,另一个是“空白页”。

2.顶部Banner

        App顶部是一个Banner,也叫轮播图,点击轮播图可以跳转到对应的详情页面。

3.List组件

        轮播图下方是一个List组件,点击其中某一项可以跳转详情页面。

4.WebView

使用系统组件@ohos.web.webview 显示网页。同时,此详情页面顶部有一个标题栏,用于显示返回键和标题。

三、实现说明

1.主界面

首先实现主界面,包含一个TabBar和上面的两个页面,以下是主界面代码:

  1. import Constants from '../Constants/Constants'
  2. import Home from './Home';
  3. import EmptyPage from './EmptyPage';
  4. @Entry
  5. @Component
  6. struct Index {
  7. @State currentTabIndex: number = 0;
  8. tabsController: TabsController;
  9. aboutToAppear(){
  10. this.tabsController = new TabsController();
  11. }
  12. @Builder TabBuilder(title: string, index: number, icon: Resource) {
  13. Column() {
  14. Image(icon)
  15. .width($r('app.float.mainPage_baseTab_size'))
  16. .height($r('app.float.mainPage_baseTab_size'))
  17. .fillColor(this.getTabBarColor(index))
  18. Text(title)
  19. .margin({ top: $r('app.float.mainPage_baseTab_top') })
  20. .fontSize($r('app.float.main_tab_fontSize'))
  21. .fontColor(this.getTabBarColor(index))
  22. }
  23. .justifyContent(FlexAlign.Center)
  24. .height($r('app.float.mainPage_barHeight'))
  25. .width(Constants.FULL_PARENT)
  26. .onClick(() => {
  27. this.currentTabIndex = index;
  28. this.tabsController.changeIndex(this.currentTabIndex)
  29. })
  30. }
  31. // 获取选项卡栏颜色
  32. getTabBarColor(index: number){
  33. if(index == this.currentTabIndex){
  34. return Color.Blue;
  35. }else{
  36. return Color.Gray;
  37. }
  38. }
  39. build() {
  40. Tabs({
  41. barPosition: BarPosition.End,
  42. controller: this.tabsController
  43. }) {
  44. // 首页
  45. TabContent() {
  46. Home()
  47. }
  48. .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
  49. .backgroundColor($r('app.color.mainPage_backgroundColor'))
  50. .tabBar(this.TabBuilder(Constants.HOME_TITLE, Constants.HOME_TAB_INDEX
  51. , $r('app.media.ic_bottom_home')))
  52. // 项目
  53. TabContent() {
  54. EmptyPage()
  55. }
  56. .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
  57. .backgroundColor($r('app.color.mainPage_backgroundColor'))
  58. .tabBar(this.TabBuilder(Constants.EMPTY_TITLE, Constants.EMPTY_TAB_INDEX
  59. , $r('app.media.ic_bottom_empty')))
  60. }
  61. .width(Constants.FULL_PARENT)
  62. .backgroundColor(Color.White)
  63. .barHeight($r('app.float.mainPage_barHeight'))
  64. .barMode(BarMode.Fixed)
  65. .onChange((index: number) => {
  66. this.currentTabIndex = index;
  67. })
  68. }
  69. }
2.首页

本文主要实现的就是首页,用竖向列包含了Banner组件和ArticleList组件,代码如下:

  1. @Component
  2. export default struct Home {
  3. build() {
  4. Column() {
  5. Banner();
  6. ArticleList();
  7. }
  8. }
  9. }

其中Banner组件代码如下,代码中有具体实现的解释:

  1. // 标记为组件
  2. @Component
  3. // 导出名为 Banner 的结构体
  4. export default struct Banner {
  5. // Swiper 控制器
  6. swiperController: SwiperController = new SwiperController();
  7. // 轮播图数据
  8. @State bannerData: HomeBannerItemBean[] = [];
  9. // 生命周期钩子,在即将显示时调用
  10. aboutToAppear() {
  11. // 获取轮播图数据
  12. this.getData();
  13. }
  14. // 异步获取轮播图数据
  15. async getData(){
  16. await HomeViewModel.getHomeBanner(Constants.GET_HOME_BANNER).then((data: HomeBannerItemBean[]) => {
  17. // 将获取到的数据赋值给轮播图数据
  18. this.bannerData = data;
  19. }).catch((err: string | Resource) => {
  20. // 如果发生错误,显示提示消息
  21. promptAction.showToast({
  22. message: err,
  23. duration: Constants.ANIMATION_DURATION
  24. });
  25. });
  26. }
  27. // 构建界面
  28. build() {
  29. // 垂直排列的列
  30. Column() {
  31. // 如果有轮播图数据且数据长度大于 0
  32. if(this.bannerData && this.bannerData.length > 0){
  33. // 使用 Swiper 组件创建轮播图
  34. Swiper(this.swiperController) {
  35. // 遍历轮播图数据
  36. ForEach(this.bannerData, (banner: HomeBannerItemBean) => {
  37. // 显示图片,并设置圆角和点击事件
  38. Image(banner.imagePath).borderRadius($r('app.float.home_swiper_borderRadius')).onClick(() => {
  39. // 点击事件:跳转到 Web 页面,并传递标题和链接参数
  40. router.pushUrl({
  41. url: 'pages/WebPage',
  42. params: {
  43. title: banner.title,
  44. src: banner.url
  45. }
  46. }, router.RouterMode.Single)
  47. })
  48. }, (img: Resource) => JSON.stringify(img.id))
  49. }
  50. // 设置轮播图的外边距、自动播放、宽度和高度
  51. .margin({top: $r('app.float.home_swiper_margin')})
  52. .autoPlay(true)
  53. .width(Constants.FULL_PARENT)
  54. .height(200)
  55. }
  56. }
  57. }
  58. }

那么在Banner组件下方的ArticleList组件,代码如下:

  1. // 标记为组件
  2. @Component
  3. // 导出名为 ArticleList 的结构体
  4. export default struct ArticleList {
  5. // 是否还有更多数据
  6. hasMore: boolean = true;
  7. // 当前页码
  8. currentPage: number = 0;
  9. // 每页数据量
  10. pageSize: number = 30;
  11. // 文章数据数组
  12. @State articleData: ArticleItemData[] = [];
  13. // 需绑定列表或宫格组件的滚动控制器
  14. private scroller: Scroller = new Scroller();
  15. // 生命周期钩子,在即将显示时调用
  16. aboutToAppear() {
  17. // 获取文章列表数据,参数为 true 表示重置数据
  18. this.getArticleList(true);
  19. }
  20. // @Builder 修饰的私有方法,用于创建列表视图
  21. @Builder
  22. private getListView() {
  23. // 使用 List 组件创建列表
  24. List({ space: 10, scroller: this.scroller }) {
  25. // 遍历文章数据
  26. ForEach(this.articleData, (item: ArticleItemData) => {
  27. // 使用 ListItem 创建列表项
  28. ListItem() {
  29. // 使用 Flex 创建布局
  30. Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, }) {
  31. // 作者和发布时间
  32. Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, }) {
  33. Text(item.author)
  34. .fontSize(13)
  35. Text(Util.dateTime(item.publishTime))
  36. .fontSize(13)
  37. }.margin({ top: 10, left: 30, right: 30 })
  38. // 文章标题
  39. Text(item.title)
  40. .width('100%')
  41. .fontSize(20)
  42. .textAlign(TextAlign.Center)
  43. .fontWeight(FontWeight.Bold)
  44. .maxLines(1)
  45. .textOverflow({ overflow: TextOverflow.Ellipsis })
  46. .margin({ left: 10, right: 10 })
  47. // 文章分类
  48. Text(item.chapterName)
  49. .fontSize(13)
  50. .textAlign(TextAlign.Start)
  51. .width('100%')
  52. .margin({ left: 30, bottom: 10 })
  53. }.borderRadius($r('app.float.home_list_borderRadius'))
  54. .backgroundColor(Color.White)
  55. }.onClick(() => {
  56. // 点击列表项跳转到 Web 页面,传递标题和链接参数
  57. router.pushUrl({
  58. url: 'pages/WebPage',
  59. params: {
  60. title: item.title,
  61. src: item.link
  62. }
  63. }, router.RouterMode.Single)
  64. })
  65. })
  66. }
  67. // 设置列表的圆角
  68. .borderRadius($r('app.float.home_list_borderRadius'))
  69. // .divider({ strokeWidth: 1, color: 0x222222 })
  70. // 设置列表的边缘效果为无效果
  71. .edgeEffect(EdgeEffect.None)
  72. // 设置列表宽度和高度为100%
  73. .width('100%')
  74. .height('100%')
  75. }
  76. // 异步获取文章列表数据
  77. async getArticleList(reset: boolean) {
  78. await HomeViewModel.getHomeArticleList(this.currentPage, this.pageSize, Constants.GET_HOME_ARTICLE_LIST)
  79. .then((data: ArticleDataBean) => {
  80. // 判断是否还有更多数据
  81. if (data.curPage < data.pageCount) {
  82. this.currentPage++;
  83. this.hasMore = true;
  84. } else {
  85. this.hasMore = false;
  86. }
  87. // 根据参数判断是否重置数据
  88. if (reset) {
  89. this.articleData = data.datas;
  90. } else {
  91. this.articleData = this.articleData.concat(data.datas);
  92. }
  93. }).catch((err: string | Resource) => {
  94. // 显示错误提示
  95. promptAction.showToast({ message: err });
  96. })
  97. }
  98. // 构建界面
  99. build() {
  100. // 使用 Column 组件创建垂直布局
  101. Column() {
  102. // 使用 PullToRefresh 组件实现下拉刷新和上拉加载
  103. PullToRefresh({
  104. customRefresh: null,
  105. customLoad: null,
  106. data: $articleData, // 数据源数组
  107. scroller: this.scroller, // 控制器,负责关闭下拉和上拉
  108. customList: () => {
  109. // 调用 getListView 方法创建列表视图
  110. this.getListView();
  111. },
  112. onRefresh: () => {
  113. return new Promise<string>((resolve, reject) => {
  114. // 下拉刷新成功后解析数据,重新获取文章列表数据
  115. resolve('下拉刷新成功')
  116. this.getArticleList(true);
  117. });
  118. },
  119. onLoadMore: () => {
  120. // 上拉加载成功后解析数据,继续获取更多文章列表数据
  121. return new Promise<string>((resolve, reject) => {
  122. resolve('上拉加载成功')
  123. this.getArticleList(false);
  124. });
  125. }
  126. })
  127. }.backgroundColor("#efefef") // 设置背景颜色
  128. }
  129. }

这个文章列表组件,使用了 PullToRefresh 组件和 List 组件展示通过网络获取的文章列表数据,并且点击其中每一项都可以跳转详情页面,使用到的系统方法是router.pushUrl。

3.网络请求

其中网络请求代码如下:

  1. /**
  2. * 发起 HTTP GET 请求的函数
  3. * @param url 请求的 URL 地址
  4. * @param extraData 额外的请求数据,可选参数
  5. * @returns 返回 Promise 对象,包含 ResponseResult 数据
  6. */
  7. export function httpRequestGet(url: string, extraData?: Record<string, any>): Promise<ResponseResult> {
  8. // 创建 HTTP 请求实例
  9. let httpRequest = http.createHttp();
  10. // 发起 HTTP GET 请求
  11. let responseResult = httpRequest.request(url, {
  12. method: http.RequestMethod.GET,
  13. readTimeout: Constants.HTTP_READ_TIMEOUT,
  14. header: {
  15. 'Content-Type': ContentType.JSON
  16. },
  17. connectTimeout: Constants.HTTP_READ_TIMEOUT,
  18. extraData: extraData || {} // 使用 extraData 参数,如果不存在则传递一个空对象
  19. });
  20. // 创建用于存储响应数据的对象
  21. let serverData: ResponseResult = new ResponseResult();
  22. // 处理返回的数据并返回结果
  23. return responseResult.then((value: http.HttpResponse) => {
  24. // 检查 HTTP 响应码是否为成功状态(200)
  25. if (value.responseCode === Constants.HTTP_CODE_200) {
  26. // 解析返回的数据
  27. let result = `${value.result}`;
  28. let resultJson: ResponseResult = JSON.parse(result);
  29. // 检查服务器返回的错误码是否为成功状态
  30. if (resultJson.errorCode === Constants.SERVER_CODE_SUCCESS) {
  31. // 将返回的数据赋值给 serverData 对象
  32. serverData.data = resultJson.data;
  33. }
  34. // 将服务器返回的错误码和错误消息赋值给 serverData 对象
  35. serverData.errorCode = resultJson.errorCode;
  36. serverData.errorMsg = resultJson.errorMsg;
  37. } else {
  38. // 设置错误消息,包含 HTTP 错误码
  39. serverData.errorMsg = `${$r('app.string.http_error_message')}&${value.responseCode}`;
  40. }
  41. // 返回处理后的 serverData 对象
  42. return serverData;
  43. }).catch((err) => {
  44. // 捕获异常,打印错误信息,并设置错误消息
  45. console.log("error:" + JSON.stringify(err));
  46. serverData.errorMsg = $r('app.string.http_error_message');
  47. // 返回处理后的 serverData 对象
  48. return serverData;
  49. });
  50. }
4.详情页面

详情页面是通过webview来展示网页实现的,代码如下:

  1. // 标记为入口
  2. @Entry
  3. // 标记为组件
  4. @Component
  5. // Web 页面结构体
  6. struct WebPage {
  7. // 从路由获取参数中的 'src',并存储到状态中
  8. @State src: string = router.getParams()?.['src'];
  9. // 从路由获取参数中的 'title',并存储到状态中
  10. @State title: string = router.getParams()?.['title'];
  11. // Web 页面控制器
  12. controller: web_webview.WebviewController = new web_webview.WebviewController();
  13. // 构建界面
  14. build() {
  15. // 使用 Column 组件创建垂直布局
  16. Column() {
  17. // 页面标题组件
  18. PageTitle({ titleName: this.title })
  19. // 分隔线
  20. Divider()
  21. .strokeWidth('1px')
  22. .color($r('sys.color.ohos_id_color_list_separator'))
  23. // Web 组件,加载指定的网址,并使用控制器进行交互
  24. Web({
  25. src: this.src, controller: this.controller
  26. }).javaScriptAccess(true)
  27. }
  28. }
  29. }
  30. // 页面标题组件结构体
  31. @Component
  32. struct PageTitle {
  33. // 标题名称
  34. private titleName: string
  35. // 构建界面
  36. build() {
  37. // 使用 Row 组件创建水平布局
  38. Row() {
  39. // 返回按钮图标
  40. Image($r('app.media.back'))
  41. .width(20)
  42. .height(20)
  43. .onClick(() => {
  44. // 点击返回按钮,触发路由返回操作
  45. router.back()
  46. })
  47. // 标题文本
  48. Text(this.titleName)
  49. .fontSize(Constants.PAGE_TITLE_TEXT_SIZE)
  50. .width(Constants.PAGE_TITLE_TEXT_WIDTH)
  51. .maxLines(Constants.PAGE_TITLE_TEXT_MAX_LINES)
  52. .textOverflow({ overflow: TextOverflow.Ellipsis })
  53. .margin({ left: 20 })
  54. }
  55. // 设置整体内边距
  56. .padding(12)
  57. // 设置整体宽度为100%
  58. .width('100%')
  59. }
  60. }

四、源码地址

WanAndroid: 基于鸿蒙ArkTS语言实现的WanAndroid App

如果觉得本文不错的话,帮忙点个赞吧~感谢~

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

闽ICP备14008679号