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

发布时间:2024年01月15日

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

本文主要内容包括:

一、效果图

首页
详情页

??

二、内容简介

1.底部Tab栏和两个页面

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

2.顶部Banner

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

3.List组件

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

4.WebView

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

三、实现说明

1.主界面

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

import Constants from '../Constants/Constants'
import Home from './Home';
import EmptyPage from './EmptyPage';
@Entry
@Component
struct Index {
  @State currentTabIndex: number = 0;
  tabsController: TabsController;
  aboutToAppear(){
    this.tabsController = new TabsController();
  }
  @Builder TabBuilder(title: string, index: number, icon: Resource) {
    Column() {
      Image(icon)
        .width($r('app.float.mainPage_baseTab_size'))
        .height($r('app.float.mainPage_baseTab_size'))
        .fillColor(this.getTabBarColor(index))
      Text(title)
        .margin({ top: $r('app.float.mainPage_baseTab_top') })
        .fontSize($r('app.float.main_tab_fontSize'))
        .fontColor(this.getTabBarColor(index))
    }
    .justifyContent(FlexAlign.Center)
    .height($r('app.float.mainPage_barHeight'))
    .width(Constants.FULL_PARENT)
    .onClick(() => {
      this.currentTabIndex = index;
      this.tabsController.changeIndex(this.currentTabIndex)
    })
  }

  // 获取选项卡栏颜色
  getTabBarColor(index: number){
    if(index == this.currentTabIndex){
      return Color.Blue;
    }else{
      return Color.Gray;
    }
  }

  build() {
    Tabs({
      barPosition: BarPosition.End,
      controller: this.tabsController
    }) {
      // 首页
      TabContent() {
        Home()
      }
      .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
      .backgroundColor($r('app.color.mainPage_backgroundColor'))
      .tabBar(this.TabBuilder(Constants.HOME_TITLE, Constants.HOME_TAB_INDEX
        , $r('app.media.ic_bottom_home')))

      // 项目
      TabContent() {
        EmptyPage()
      }
      .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
      .backgroundColor($r('app.color.mainPage_backgroundColor'))
      .tabBar(this.TabBuilder(Constants.EMPTY_TITLE, Constants.EMPTY_TAB_INDEX
        , $r('app.media.ic_bottom_empty')))
    }
    .width(Constants.FULL_PARENT)
    .backgroundColor(Color.White)
    .barHeight($r('app.float.mainPage_barHeight'))
    .barMode(BarMode.Fixed)
    .onChange((index: number) => {
      this.currentTabIndex = index;
    })
  }
}
2.首页

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

@Component
export default struct Home {
  build() {
    Column() {
      Banner();
      ArticleList();
    }
  }
}

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

// 标记为组件
@Component
  // 导出名为 Banner 的结构体
export default struct Banner {
  // Swiper 控制器
  swiperController: SwiperController = new SwiperController();
  // 轮播图数据
  @State bannerData: HomeBannerItemBean[] = [];

  // 生命周期钩子,在即将显示时调用
  aboutToAppear() {
    // 获取轮播图数据
    this.getData();
  }

  // 异步获取轮播图数据
  async getData(){
    await HomeViewModel.getHomeBanner(Constants.GET_HOME_BANNER).then((data: HomeBannerItemBean[]) => {
      // 将获取到的数据赋值给轮播图数据
      this.bannerData = data;
    }).catch((err: string | Resource) => {
      // 如果发生错误,显示提示消息
      promptAction.showToast({
        message: err,
        duration: Constants.ANIMATION_DURATION
      });
    });
  }

  // 构建界面
  build() {
    // 垂直排列的列
    Column() {
      // 如果有轮播图数据且数据长度大于 0
      if(this.bannerData && this.bannerData.length > 0){
        // 使用 Swiper 组件创建轮播图
        Swiper(this.swiperController) {
          // 遍历轮播图数据
          ForEach(this.bannerData, (banner: HomeBannerItemBean) => {
            // 显示图片,并设置圆角和点击事件
            Image(banner.imagePath).borderRadius($r('app.float.home_swiper_borderRadius')).onClick(() => {
              // 点击事件:跳转到 Web 页面,并传递标题和链接参数
              router.pushUrl({
                url: 'pages/WebPage',
                params: {
                  title: banner.title,
                  src: banner.url
                }
              }, router.RouterMode.Single)
            })
          }, (img: Resource) => JSON.stringify(img.id))
        }
        // 设置轮播图的外边距、自动播放、宽度和高度
        .margin({top: $r('app.float.home_swiper_margin')})
        .autoPlay(true)
        .width(Constants.FULL_PARENT)
        .height(200)
      }
    }
  }
}

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

// 标记为组件
@Component
// 导出名为 ArticleList 的结构体
export default struct ArticleList {
  // 是否还有更多数据
  hasMore: boolean = true;
  // 当前页码
  currentPage: number = 0;
  // 每页数据量
  pageSize: number = 30;
  // 文章数据数组
  @State articleData: ArticleItemData[] = [];
  // 需绑定列表或宫格组件的滚动控制器
  private scroller: Scroller = new Scroller();

  // 生命周期钩子,在即将显示时调用
  aboutToAppear() {
    // 获取文章列表数据,参数为 true 表示重置数据
    this.getArticleList(true);
  }

  // @Builder 修饰的私有方法,用于创建列表视图
  @Builder
  private getListView() {
    // 使用 List 组件创建列表
    List({ space: 10, scroller: this.scroller }) {
      // 遍历文章数据
      ForEach(this.articleData, (item: ArticleItemData) => {
        // 使用 ListItem 创建列表项
        ListItem() {
          // 使用 Flex 创建布局
          Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween, }) {
            // 作者和发布时间
            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, }) {
              Text(item.author)
                .fontSize(13)
              Text(Util.dateTime(item.publishTime))
                .fontSize(13)
            }.margin({ top: 10, left: 30, right: 30 })

            // 文章标题
            Text(item.title)
              .width('100%')
              .fontSize(20)
              .textAlign(TextAlign.Center)
              .fontWeight(FontWeight.Bold)
              .maxLines(1)
              .textOverflow({ overflow: TextOverflow.Ellipsis })
              .margin({ left: 10, right: 10 })

            // 文章分类
            Text(item.chapterName)
              .fontSize(13)
              .textAlign(TextAlign.Start)
              .width('100%')
              .margin({ left: 30, bottom: 10 })
          }.borderRadius($r('app.float.home_list_borderRadius'))
          .backgroundColor(Color.White)
        }.onClick(() => {
          // 点击列表项跳转到 Web 页面,传递标题和链接参数
          router.pushUrl({
            url: 'pages/WebPage',
            params: {
              title: item.title,
              src: item.link
            }
          }, router.RouterMode.Single)
        })
      })
    }
    // 设置列表的圆角
    .borderRadius($r('app.float.home_list_borderRadius'))
    // .divider({ strokeWidth: 1, color: 0x222222 })
    // 设置列表的边缘效果为无效果
    .edgeEffect(EdgeEffect.None)
    // 设置列表宽度和高度为100%
    .width('100%')
    .height('100%')
  }

  // 异步获取文章列表数据
  async getArticleList(reset: boolean) {
    await HomeViewModel.getHomeArticleList(this.currentPage, this.pageSize, Constants.GET_HOME_ARTICLE_LIST)
      .then((data: ArticleDataBean) => {
        // 判断是否还有更多数据
        if (data.curPage < data.pageCount) {
          this.currentPage++;
          this.hasMore = true;
        } else {
          this.hasMore = false;
        }
        // 根据参数判断是否重置数据
        if (reset) {
          this.articleData = data.datas;
        } else {
          this.articleData = this.articleData.concat(data.datas);
        }
      }).catch((err: string | Resource) => {
        // 显示错误提示
        promptAction.showToast({ message: err });
      })
  }

  // 构建界面
  build() {
    // 使用 Column 组件创建垂直布局
    Column() {
      // 使用 PullToRefresh 组件实现下拉刷新和上拉加载
      PullToRefresh({
        customRefresh: null,
        customLoad: null,
        data: $articleData, // 数据源数组
        scroller: this.scroller, // 控制器,负责关闭下拉和上拉
        customList: () => {
          // 调用 getListView 方法创建列表视图
          this.getListView();
        },
        onRefresh: () => {
          return new Promise<string>((resolve, reject) => {
            // 下拉刷新成功后解析数据,重新获取文章列表数据
            resolve('下拉刷新成功')
            this.getArticleList(true);
          });
        },
        onLoadMore: () => {
          // 上拉加载成功后解析数据,继续获取更多文章列表数据
          return new Promise<string>((resolve, reject) => {
            resolve('上拉加载成功')
            this.getArticleList(false);
          });
        }
      })
    }.backgroundColor("#efefef") // 设置背景颜色
  }
}

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

3.网络请求

其中网络请求代码如下:

/**
 * 发起 HTTP GET 请求的函数
 * @param url 请求的 URL 地址
 * @param extraData 额外的请求数据,可选参数
 * @returns 返回 Promise 对象,包含 ResponseResult 数据
 */
export function httpRequestGet(url: string, extraData?: Record<string, any>): Promise<ResponseResult> {
  // 创建 HTTP 请求实例
  let httpRequest = http.createHttp();
  
  // 发起 HTTP GET 请求
  let responseResult = httpRequest.request(url, {
    method: http.RequestMethod.GET,
    readTimeout: Constants.HTTP_READ_TIMEOUT,
    header: {
      'Content-Type': ContentType.JSON
    },
    connectTimeout: Constants.HTTP_READ_TIMEOUT,
    extraData: extraData || {}  // 使用 extraData 参数,如果不存在则传递一个空对象
  });

  // 创建用于存储响应数据的对象
  let serverData: ResponseResult = new ResponseResult();

  // 处理返回的数据并返回结果
  return responseResult.then((value: http.HttpResponse) => {
    // 检查 HTTP 响应码是否为成功状态(200)
    if (value.responseCode === Constants.HTTP_CODE_200) {
      // 解析返回的数据
      let result = `${value.result}`;
      let resultJson: ResponseResult = JSON.parse(result);
      
      // 检查服务器返回的错误码是否为成功状态
      if (resultJson.errorCode === Constants.SERVER_CODE_SUCCESS) {
        // 将返回的数据赋值给 serverData 对象
        serverData.data = resultJson.data;
      }
      // 将服务器返回的错误码和错误消息赋值给 serverData 对象
      serverData.errorCode = resultJson.errorCode;
      serverData.errorMsg = resultJson.errorMsg;
    } else {
      // 设置错误消息,包含 HTTP 错误码
      serverData.errorMsg = `${$r('app.string.http_error_message')}&${value.responseCode}`;
    }
    // 返回处理后的 serverData 对象
    return serverData;
  }).catch((err) => {
    // 捕获异常,打印错误信息,并设置错误消息
    console.log("error:" + JSON.stringify(err));
    serverData.errorMsg = $r('app.string.http_error_message');
    // 返回处理后的 serverData 对象
    return serverData;
  });
}
4.详情页面

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

// 标记为入口
@Entry
// 标记为组件
@Component
// Web 页面结构体
struct WebPage {
  // 从路由获取参数中的 'src',并存储到状态中
  @State src: string = router.getParams()?.['src'];
  // 从路由获取参数中的 'title',并存储到状态中
  @State title: string = router.getParams()?.['title'];

  // Web 页面控制器
  controller: web_webview.WebviewController = new web_webview.WebviewController();

  // 构建界面
  build() {
    // 使用 Column 组件创建垂直布局
    Column() {
      // 页面标题组件
      PageTitle({ titleName: this.title })

      // 分隔线
      Divider()
        .strokeWidth('1px')
        .color($r('sys.color.ohos_id_color_list_separator'))

      // Web 组件,加载指定的网址,并使用控制器进行交互
      Web({
        src: this.src, controller: this.controller
      }).javaScriptAccess(true)
    }
  }
}

// 页面标题组件结构体
@Component
struct PageTitle {
  // 标题名称
  private titleName: string

  // 构建界面
  build() {
    // 使用 Row 组件创建水平布局
    Row() {
      // 返回按钮图标
      Image($r('app.media.back'))
        .width(20)
        .height(20)
        .onClick(() => {
          // 点击返回按钮,触发路由返回操作
          router.back()
        })

      // 标题文本
      Text(this.titleName)
        .fontSize(Constants.PAGE_TITLE_TEXT_SIZE)
        .width(Constants.PAGE_TITLE_TEXT_WIDTH)
        .maxLines(Constants.PAGE_TITLE_TEXT_MAX_LINES)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .margin({ left: 20 })
    }
    // 设置整体内边距
    .padding(12)
    // 设置整体宽度为100%
    .width('100%')
  }
}

四、源码地址

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

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

文章来源:https://blog.csdn.net/liuhaikang/article/details/135591475
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。