0基础,从0到1探索鸿蒙应用,不一样的探索过程。第三篇

284 阅读7分钟

第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载

第一篇:创建一个应用,使用Tabs和导航栏结构

第二篇:使用网格Grid与列表list

第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载

第四篇:整理工程 - 模块化

第五篇:探索打包提审上架

背景:鸿蒙0基础探索。

愿景:本系列旨在用简单直接的方式从让鸿蒙app先从0-1。

理由:有产出更有继续下去的动力。

记录:每篇文章保留下探索的过程。

PS:  不教写代码,不封装,业务代码能力因人而异。(关键我不会啊😁)

书接上回。 我们现在学会了使用Tabs,Navigation,Grid。有了这几件的基础,自然就可以探索网络请求,和模型解析。(这一篇探索完是不是感觉写个小应用没啥问题了。)

探索

说到网络请求,介于iOS经验,第一步我想到的就是使用网络请求的第三方库。(毕竟在项目中,真的很少用自带的直接进行请求)。顺便也尝试一下怎么集成第三方库。

通过搜索鸿蒙第三方库就会找到,OpenHarmony三方库中心仓

怎么安装包文档

一进来我们就发现了想要的,说明这个探索的路子是没错的。网络请求库:ohos/axios。额外补充一句这个ohos打头的不知道是不是官方维护的,因为看到还有别的知名库。比如lottie。那就愉快的决定使用ohos/axios了。

image.png

没有后台支持,接下来我要去找一个免费的API的接口。经过一番搜索。找到了github上免费api集合 我挑了其中一个Animals - Cats的。Cats文档, 获取免费的api key申请,填邮箱,会把apikey发送到邮箱。

ps:自己有后台,有其他接口用就不用这么麻烦了。我这就做了示例。

导入Axios,进行网络请求。

axios三方库中心文档

axios码云文档 :这个看起来更舒服。

怎么安装包文档中提到了几种安装方式。 我们结合着Axios的文档,选择用终端区安装,如下图所示,导入第三方是不是很简单。

image.png

文档上: 需要权限 ohos.permission.INTERNET, 不知道怎么做,老样子,我们先去官方文档上搜索一下,把前几个链接都点开扫一眼,找找关键词。我找到了这个。

image.png

于是乎我们也在module.json5中,添加这个配置。记得点Sync Now.

发起请求

使用实例中有这么一段描述

发起一个 GET 请求

axios支持泛型参数,由于ArkTS不再支持any类型,需指定参数的具体类型。 如:axios.get<T = any, R = AxiosResponse, D = any>(url)

  • T: 是响应数据类型。当发送一个 POST 请求时,客户端可能会收到一个 JSON 对象。T 就是这个 JSON 对象的类型。默认情况下,T 是 any,这意味着可以接收任何类型的数据。
  • R: 是响应体的类型。当服务器返回一个响应时,响应体通常是一个 JSON 对象。R 就是这个 JSON 对象的类型。默认情况下,R 是 AxiosResponse,这意味着响应体是一个 AxiosResponse 对象,它的 data 属性是 T 类型的
  • D: 是请求参数的类型。当发送一个 GET 请求时,可能会在 URL 中添加一些查询参数。D 就是这些查询参数的类型。参数为空情况下,D 是 null类型。

我们现在对这样一个Api发起请求api.thecatapi.com/v1/images/s… ps:返回的列表里图片怎么我用wifi不行,你也不行换几个网络环境试试。

我们不先定义模型,直接去发送请求(也不做任何额外的定义,先看能不能调通才是第一要素),通过log看看输出。

@Entry
@Component
export struct CatSqualPage {
  
  // 页面出现的时候
  aboutToAppear(): void {
    // 去发送请求,
    axios.get("https://api.thecatapi.com/v1/images/search", {params: { limit: "10"} })
      .then((response: AxiosResponse<catInfo[]>) => {
        console.warn("请求:" + JSON.stringify(response.config));
        console.warn("请求结果result:" + JSON.stringify(response.data));
        console.warn("请求结果类型result:" + typeof response.data );
        // 便利打印
        response.data.forEach(cat => {
          console.warn(`ID: ${cat.id}, URL: ${cat.url}, Width: ${cat.width}, Height: ${cat.height}`);
        });
      })
      .catch((error: AxiosError) => {
        console.error("result:" + error.message);
      });
  }

  build() {

  }
}

能调通

image.png

模型解析

那接着我们看看接口返回的数据,想着给他做模型化。不得不说这库对解析的集成,真挺好用。比起iOS来说。

ps: 据说之前版本支持any,接口不需要做模型化也能用。现在Arkts禁用了any。

[
    {
        "id": "140",
        "url": "https://cdn2.thecatapi.com/images/140.gif",
        "width": 293,
        "height": 163
    }
]

结合上两篇我们所学的,用到NavDestination做导航子组件,Grid网格承接模型数据。网络请求成功后,刷新网格的数据,因此涉及到了数据绑定@State.

  • @State: 可能类似于双向绑定,响应式编程这样的概念。值改变,自动触发页面改变。
  • UI范式基本语法: 有时间最好把这看看(我没看,因为这系列致力于探索遇到问题解决问题)

看看文档使用示例T,R应该都是泛型的意思。只需要声明一下,response.data就可以直接拿来用,真是方便呢。比如示例中:

axios.get<userInfo, AxiosResponse<userInfo>, null>(参数1, 参数2)

image.png

示例代码:

import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from '@ohos/axios'

@Builder
export function CatSqualPageBuilder() {
  CatSqualPage()
}

// 自己创建一个示例,也可以用axios直接调用,我是为了练习,看看能配置些什么。
const gxwAxios = axios.create({
  baseURL: 'https://api.thecatapi.com',
  timeout: 3000,
})

// 实体类(猫的模型)
interface catInfo {
  id: string,
  url: string,
  width: number,
  height: number
}

@Entry
@Component
export struct CatSqualPage {

  // 数据源
  @State modelArray: catInfo[] = []

  // 页面出现的时候
  aboutToAppear(): void {
    // 请求猫的数据
    let config: AxiosRequestConfig = {
      method: 'get',
      params: {
        limit : "10",
        // 这两行注释打开就是另一个接口数据咯
        //breed_ids: "beng",
        //api_key: "live_uRCza6dSXUkli9FSLkPYL24UePJN2b9AD4D7hcanPKTfgV0v3jgS9hkCMd1Dp0bq"
      }
    }

    //
    gxwAxios.get<catInfo, AxiosResponse<catInfo[]>>("/v1/images/search", config)
      .then((response: AxiosResponse<catInfo[]>) => {
        console.warn("请求:" + JSON.stringify(response.config));
        console.warn("请求结果result:" + JSON.stringify(response.data));
        console.warn("请求结果类型result:" + typeof response.data );
        this.modelArray = response.data
        // 便利打印
        this.modelArray.forEach(cat => {
          console.warn(`ID: ${cat.id}, URL: ${cat.url}, Width: ${cat.width}, Height: ${cat.height}`);
        });
    })
      .catch((error: AxiosError) => {
        console.error("result:" + error.message);
      });
  }

  build() {
    NavDestination() {
      Column() {
        Grid() {
          ForEach(this.modelArray, (item: catInfo, index) => {
            GridItem() {
              Column() {
                Image(item.url)
                  // 设置占位图。
                  .alt($rawfile('草/c1.png'))
              }
            }
            .height('30%')
          })
        }
        .columnsTemplate('1fr 1fr')
        .rowsGap(10)
        .columnsGap(20)
        .backgroundColor(Color.Yellow)
      }
      .backgroundColor(Color.Red)
    }
  }
}

占位图

经过对文档的一番搜索只在api参考里发现 ImageSpan控件,api参考中给出了一个示例,奇怪的是在指南中为什么没有提这个。通过观察ImageSpan是通过.alt属性设置占位图的,我就试了试Image有没有这个属性,发现是有的。Image的alt属性介绍在这里

Image(item.url)
  // 占位图
  .alt($rawfile('火/h1.png'))

上拉加载下拉刷新

接口请求之后,自然就联想到了分页,刷新和加载更多的操作。

遇事不决就搜索搜索刷新关键词其结果都为我们指向了PullToRefresh,正巧还能让我再次练习使用第三方库。

PullToRefresh代码文档

网络请求,网格,下拉刷新上拉加载代码都加上会有些凌乱,我们稍微调整一下。

比如:把网络请求写成一个function

ps: 我不会写Pormise或其他,只能模仿着iOS的Block来写。

// MARK: 请求数据
// 请求猫的数据,支持分页的数据。
requestCatsData(page: number, complete: (array: catInfo[]) => void, failure?: (error: AxiosError) => void ) {
  // 参数
  let config: AxiosRequestConfig = {
    method: 'get',
    params: {
      page: page,
      limit : "10",
      // 这两行注释打开就是另一个接口数据咯
      breed_ids: "beng",
      api_key: "live_uRCza6dSXUkli9FSLkPYL24UePJN2b9AD4D7hcanPKTfgV0v3jgS9hkCMd1Dp0bq"
    }
  }

  // 发送请求
  gxwAxios.get<catInfo, AxiosResponse<catInfo[]>>("/v1/images/search", config)
    .then((response: AxiosResponse<catInfo[]>) => {
      console.warn("请求:" + JSON.stringify(response.config));
      console.warn("请求结果result:" + JSON.stringify(response.data));
      console.warn("请求结果类型result:" + typeof response.data );
      let result = response.data
      // 便利打印
      result.forEach(cat => {
        console.warn(`ID: ${cat.id}, URL: ${cat.url}, Width: ${cat.width}, Height: ${cat.height}`);
      });
      complete(result)
    })
    .catch((error: AxiosError) => {
      if (failure) {
        failure(error)
      }
    });
}

把网格做成一个@Builder, 这有点像iOS中用一个UIView把控件包装一层 注意这个Grid(this.scroller) .edgeEffect(EdgeEffect.None)这两行,我看了文档介入之后上下都拉不动,把PullToRefresh的demo下载下来一点点比才发现是这里。

// 网格
@Builder
creatMyGrid() {
  Grid(**this.scroller**) {
    ForEach(this.modelArray, (item: catInfo, index) => {
      GridItem() {
        Column() {
          Image(item.url)
            // 设置占位图。
            .alt($rawfile('草/c1.png'))
        }
      }
      .height('30%')
    })
  }
  .columnsTemplate('1fr 1fr')
  .rowsGap(10)
  .columnsGap(20)
  .width('100%')
  .height('100%')
  .edgeEffect(EdgeEffect.None)
  .backgroundColor(Color.Yellow)
}

加载中loading

loading需要在组件的上面,也就是需要有一个上下的层级关系。那我在布局中找到了Stack

  • 层叠布局Stack
  • LoadingProgress: 我在文档中随便找到组件,通用的不一定是用这个组件,而且有了Stack布局,可以用自己写的任意加载组件。
  • 再配上@State 一个boolean值,就可以控制LoadingProgress的显示隐藏。
  • 刚开始我是用if写判断,控制是否展示LoadingProgress()组件, 后来发现他有enableLoading看起来能用,试了一下,确实能用。

我的示例代码:


@Entry
@Component
export struct CatSqualPage {

  @State shouldShowLoading: boolean = true

  aboutToAppear(): void {
    // 模拟网络请求,做一个3秒延迟
    setTimeout(() => {
      this.shouldShowLoading = false
    }, 3000);
  }

  build() {
    NavDestination() {
      Stack() {
        Text("我试试层叠布局哈哈哈哈")
        LoadingProgress().enableLoading(this.shouldShowLoading)
          .color(Color.Blue)
          .layoutWeight(1)
          .width('60%')
      }
      .width('100%')
      .height('100%')
    }
  }
}

空状态页,请求失败页

关于空态页,我没找到有什么方便的方案,也不准备花太多时间,毕竟现在只是预习,随着阅读更多资料,早晚会看到好的解决方法。

我临时的方案:还是通过监听数据源的有无,在配合Stack或者.overlay(),来加载空态组件。

// 网格
@Builder
creatMyGrid() {
  Stack() {
    Grid(this.scroller) {
      ForEach(this.modelArray, (item: catInfo, index) => {
        GridItem() {
          Column() {
            Image(item.url)
              // 设置占位图。
              .alt($rawfile('草/c1.png'))
          }
        }
        .height('30%')
      })
    }
    .columnsTemplate('1fr 1fr')
    .rowsGap(10)
    .columnsGap(20)
    .edgeEffect(EdgeEffect.None)
    .backgroundColor(Color.Yellow)

    // 设置空态页
    if (this.modelArray.length == 0) {
      Column({space: 20}) {
        Image($rawfile('火/h1.png'))
          .height('30%')
        Text("暂无数据")
      }
    }

    // 设置是否loading
    if (this.shouldShowLoading) {
      LoadingProgress()
        .color(Color.Blue)
        //.layoutWeight(1)
        .width('35%')
    }

  }
  .width('100%')
  .height('100%')
  .alignContent(Alignment.Center)
}

看看我们这一期的效果

网络请求 + 占位图 + 分页 + 下拉刷新上拉加载 + loading + 空态页

录屏2024-08-28 17.17.13.gif

Demo代码

最后附上demo地址,第三篇内容,把用得上几个文件拷进自己新建的工程就行。别忘了module.json5里加 "routerMap": "$profile:route_map",。 还有route_map.json文件别忘了。 别忘了安卓三方库ohpm install @ohos/axios 和 ohpm install @ohos/pulltorefresh