鸿蒙App开发 HarmonyOS:网络请求+三方库使用+底栏切换+列表+Banner

本篇的大致内容如下:

  1. 效果展示
  2. 知识点分解
  3. 具体实现
  4. 最后总结

一. 效果展示

二. 知识点分解

1. 基础组件使用

  • Tabs + TabContent

以Tabs组件为基础,其中有2个TAB分别展示了"首页"TAB和"项目"TAB,本例中项目TAB下无内容。仅作为展示Tabs组件所用。

Tabs通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。 需要配合TabContent组件使用,TabContent仅在Tabs中使用,对应一个切换页签的内容视图。

  • Swiper

首页顶部放置的Swiper组件, 滑块视图容器,提供子组件滑动轮播显示的能力。

  • Web

提供具有网页显示能力的Web组件,@ohos.web.webview 提供web控制能力。

2. 页面跳转

@system.router (页面路由) 通过不同的uri访问不同的页面。

本例中,在点击Swiper组件内容或文章列表项后,跳转到应用内的Web页面。

3. 网络请求

@ohos.net.http (数据请求)

参考鸿蒙官方sample代码,对http数据请求进行了简单封装。可以进行get类型的请求发送。

4. 引入三方库

由于鸿蒙原生List控件并没有下拉刷新和上拉加载功能,并且在开发者官网给出支持下拉刷新和上拉加载的demo使用起来并不方便。所以本例中使用了三方库,该引入的三方库在实现列表下拉和上拉操作时会方便许多。

这也是三方库能够加速app开发的重要特性。 本例中就以引入该三方列表库来展示如何在鸿蒙开发中引入静态三方库。

三. 具体实现

首先需要建立首页结构。在MainPage中加入Tabs组件,在Tabs中加入TabContent.

@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)
  })
}

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() {
      Project()
    }
    .padding({ left: $r('app.float.mainPage_padding'), right: $r('app.float.mainPage_padding') })
    .backgroundColor($r('app.color.mainPage_backgroundColor'))
    .tabBar(this.TabBuilder(Constants.PROJECT_TITLE, Constants.PROJECT_TAB_INDEX
      , $r('app.media.ic_bottom_project')))
  }
  .width(Constants.FULL_PARENT)
  .backgroundColor(Color.White)
  .barHeight($r('app.float.mainPage_barHeight'))
  .barMode(BarMode.Fixed)
  .onChange((index: number) => {
    this.currentTabIndex = index;
  })
}

Home中由一个Banner自定义组件和ArticleList自定义组件构成

@Component
export default struct Home {
  build() {
    Stack() {
      ArticleList();
      Banner();
    }.alignContent(Alignment.Top)
  }
}

Banner组件中进行Banner数据请求并填充到Swiper组件中。

aboutToAppear生命周期中进行了请求发送。

对于组件的 aboutToAppear 和 aboutToDisappear 在开发过程中会比较常用到。

aboutToAppear

aboutToAppear?(): void

aboutToAppear函数在创建自定义组件的新实例后,在执行其build函数之前执行。允许在aboutToAppear函数中改变状态变量,更改将在后续执行build函数中生效。

aboutToDisappear

aboutToDisappear?(): void

aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。

点击Banner项时将进行页面跳转

aboutToAppear() {
  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() {
    Swiper(this.swiperController) {
      ForEach(this.bannerData, (banner: HomeBannerItemBean) => {
        Image(banner.imagePath).borderRadius($r('app.float.home_swiper_borderRadius')).onClick(() => {
          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($r('app.float.main_swiper_height'))
  }
}

文章列表实现, 也是在aboutToAppear中进行数据请求。可以看到这里使用的三方组件ListView很轻松的实现了目标功能。

aboutToAppear() {
  this.getHomeArticleList(true);
}

@Builder
itemLayout(item, index) {
  ArticleItem({articleData: item})
}

getHomeArticleList(reset: boolean) {
  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() {
  ListView({
    items: this.articleData, //数据源 数组
    itemLayout: (item, index) => this.itemLayout(item, index),
    controller: this.controller, //控制器,负责关闭下拉和上拉
    marginHeader: 160,
    onRefresh: () => {
      //下拉刷新
      this.getHomeArticleList(true);
      this.controller.finishRefresh()
    },
    onLoadMore: () => {
      //上拉加载
      this.getHomeArticleList(false);
      this.controller.finishLoadMore()
    }
  })
}

新建WebPage, 封装Web组件实现网页的展示。 WebPage接受src参数作为页面url, 接受title参数作为标题栏文字。

import router from '@ohos.router';
import web_webview from '@ohos.web.webview'
import Constants from '../common/Constants';

@Entry
@Component
struct WebPage {
  @State src: string = router.getParams()?.['src'];
  @State title: string = router.getParams()?.['title'];

  controller: web_webview.WebviewController = new web_webview.WebviewController();

  build() {
    Column() {
      PageTitle({ titleName: this.title })

      Divider()
        .strokeWidth('1px')
        .color($r('sys.color.ohos_id_color_list_separator'))

      Web({
        src: this.src, controller: this.controller
      }).javaScriptAccess(true)
    }
  }
}

@Component
struct PageTitle {
  private titleName: string

  build() {
    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)
    .width('100%')
  }
}

别忘记在pages配置中添加WebPage页面

该配置位于ets/resources/base/profile/main_pages.json

{
  "src": [
    "pages/MainPage",
    "pages/WebPage"
  ]
}

网络请求方法封装, 实际项目中可以考虑换成其他三方库来实现网络请求。以实现Header拦截,error拦截等操作,以及其他如post类型等其他常见需求。

本例中使用的开放接口来自WanAndroid大神的无私奉献,此例中仅使用了2个接口。大家如有兴趣可以参考并自行实现全部接口。

感谢鸿洋大佬的WanAndroid网站提供的 传送门

/**
 * Initiates an HTTP request to a given URL.
 *
 * @param url URL for initiating an HTTP request.
 * @param params Params for initiating an HTTP request.
 */
export function httpRequestGet(url: string): Promise<ResponseResult> {
  let httpRequest = http.createHttp();
  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: {}
  });
  let serverData: ResponseResult = new ResponseResult();
  // Processes the data and returns.
  return responseResult.then((value: http.HttpResponse) => {
    if (value.responseCode === Constants.HTTP_CODE_200) {
      // Obtains the returned data.
      let result = `${value.result}`;
      let resultJson: ResponseResult = JSON.parse(result);
      if (resultJson.errorCode === Constants.SERVER_CODE_SUCCESS) {
        serverData.data = resultJson.data;
      }
      serverData.errorCode = resultJson.errorCode;
      serverData.errorMsg = resultJson.errorMsg;
    } else {
      serverData.errorMsg = `${$r('app.string.http_error_message')}&${value.responseCode}`;
    }
    return serverData;
  }).catch(() => {
    serverData.errorMsg = $r('app.string.http_error_message');
    return serverData;
  })
}

介绍下三方库的引入方法:

目前可以使用静态包下载后拷贝到项目中的方法来实现引入三方库。 首先在entry目录下新建一个文件夹,比如libs。将下载的har包复制进去。 然后打开entry目录下的oh-package.json5文件。在dependencies字段中填入引入的har包的地址。

完成之后进行一次sync操作。引入的三方静态包将会被以代码形式加入到项目中。

四. 最后总结

鸿蒙基础UI组件使用+页面跳转+网络数据请求+三方组件引入,这一套基础操作在着手去写App时是必不可少的。 熟练掌握后便可继续探索鸿蒙开发。刚开始还比较粗糙请多包涵!

为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. 应用开发导读(Java)

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ......

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ......

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ......
相关推荐
Lostgreen3 分钟前
计算机网络----基本概念
网络·笔记·学习·计算机网络
sxy1993sxy20186 分钟前
HTTP请求失败调试过程 -20241126
网络·网络协议·http
孤邑17 分钟前
【Linux】网络通信
linux·服务器·网络·笔记·学习
网络研究院31 分钟前
防御网络攻击的创新策略
网络·安全·攻击·风险·威胁·策略
C-200240 分钟前
selinux和防火墙
linux·服务器·网络
搬砖的果果1 小时前
HTTP代理是什么,主要用来干嘛?
网络·python·网络协议·tcp/ip·http
ChinaDragonDreamer1 小时前
HarmonyOS:应用沙箱
开发语言·harmonyos·鸿蒙
李洋-蛟龙腾飞公司1 小时前
HarmonyOS Next元服务大学之道动卡互动
华为·harmonyos
好看资源平台1 小时前
鸿蒙心路旅程:从实践到创新——开发者的深度技术分享
华为·harmonyos