第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载
第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载
背景:鸿蒙0基础探索。
愿景:本系列旨在用简单直接的方式从让鸿蒙app先从0-1。
理由:有产出更有继续下去的动力。
记录:每篇文章保留下探索的过程。
PS: 不教写代码,不封装,业务代码能力因人而异。(关键我不会啊😁)
书接上回。 我们现在学会了使用Tabs,Navigation,Grid。有了这几件的基础,自然就可以探索网络请求,和模型解析。(这一篇探索完是不是感觉写个小应用没啥问题了。)
探索
说到网络请求,介于iOS经验,第一步我想到的就是使用网络请求的第三方库。(毕竟在项目中,真的很少用自带的直接进行请求)。顺便也尝试一下怎么集成第三方库。
通过搜索鸿蒙第三方库就会找到,OpenHarmony三方库中心仓
一进来我们就发现了想要的,说明这个探索的路子是没错的。网络请求库:ohos/axios。额外补充一句这个ohos打头的不知道是不是官方维护的,因为看到还有别的知名库。比如lottie。那就愉快的决定使用ohos/axios了。
没有后台支持,接下来我要去找一个免费的API的接口。经过一番搜索。找到了github上免费api集合 我挑了其中一个Animals - Cats的。Cats文档, 获取免费的api key申请,填邮箱,会把apikey发送到邮箱。
ps:自己有后台,有其他接口用就不用这么麻烦了。我这就做了示例。
导入Axios,进行网络请求。
axios码云文档 :这个看起来更舒服。
怎么安装包文档中提到了几种安装方式。 我们结合着Axios的文档,选择用终端区安装,如下图所示,导入第三方是不是很简单。
文档上: 需要权限 ohos.permission.INTERNET, 不知道怎么做,老样子,我们先去官方文档上搜索一下,把前几个链接都点开扫一眼,找找关键词。我找到了这个。
于是乎我们也在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() {
}
}
能调通
模型解析
那接着我们看看接口返回的数据,想着给他做模型化。不得不说这库对解析的集成,真挺好用。比起iOS来说。
ps: 据说之前版本支持any,接口不需要做模型化也能用。现在Arkts禁用了any。
[
{
"id": "140",
"url": "https://cdn2.thecatapi.com/images/140.gif",
"width": 293,
"height": 163
}
]
结合上两篇我们所学的,用到NavDestination做导航子组件,Grid网格承接模型数据。网络请求成功后,刷新网格的数据,因此涉及到了数据绑定@State.
看看文档使用示例T,R应该都是泛型的意思。只需要声明一下,response.data就可以直接拿来用,真是方便呢。比如示例中:
axios.get<userInfo, AxiosResponse<userInfo>, null>(参数1, 参数2)
示例代码:
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,正巧还能让我再次练习使用第三方库。
网络请求,网格,下拉刷新上拉加载代码都加上会有些凌乱,我们稍微调整一下。
比如:把网络请求写成一个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 + 空态页
Demo代码
最后附上demo地址,第三篇内容,把用得上几个文件拷进自己新建的工程就行。别忘了module.json5里加 "routerMap": "$profile:route_map",。 还有route_map.json文件别忘了。 别忘了安卓三方库ohpm install @ohos/axios 和 ohpm install @ohos/pulltorefresh