前言
DevEco Studio版本:4.0.0.600
WanAndroid的API链接:玩Android 开放API-玩Android - wanandroid.com
其他篇文章参考:
效果
我的页面实现
从UI效果上可以看出是头部的背景+头像 和下面的业务item,因此整体可以使用RelativeContainer布局,业务item可以用column实现。
1、UI界面实现
build() {
RelativeContainer() {
Image($r('app.media.ic_background_mine'))
.width('100%')
.height(240)
.id('imageBg')
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
CircleImageView({
src: $r("app.media.ic_icon"), //本地图片资源
radius: 60,
border_Width: 1,
border_Color: Color.Pink,
}).id('imageIcon')
.margin({ top: 50 })
.alignRules({
top: { anchor: '__container__', align: VerticalAlign.Top },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
Text(decodeURIComponent(this.userName))
.fontSize(20)
.fontColor(Color.Black)
.fontWeight(FontWeight.Bold)
.id('textUsername')
.margin({ top: 20 })
.alignRules({
top: { anchor: 'imageIcon', align: VerticalAlign.Bottom },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
Column() {
Row() {
Text('我的收藏')
.fontColor(Color.Black)
.margin({ left: 15 })
Image($r("app.media.ic_mine_next"))
.width(25)
.height(22)
.margin({ right: 15 })
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height(50)
.margin({ top: 10 })
.borderRadius(10)
.backgroundColor('#66CCCCCC')
.onClick(() => {
if (this.isLogin) {
router.pushUrl({ url: 'pages/mine/MineCollectPage' })
} else {
router.pushUrl({
url: 'pages/LoginPage',
params: {
hideJump: true
}
})
}
})
Row() {
Text('个人积分')
.fontColor(Color.Black)
.margin({ left: 15 })
Image($r("app.media.ic_mine_next"))
.width(25)
.height(22)
.margin({ right: 15 })
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height(50)
.margin({ top: 20 })
.borderRadius(10)
.backgroundColor('#66CCCCCC')
.onClick(() => {
if (this.isLogin) {
router.pushUrl({ url: 'pages/mine/MineIntegralPage' })
} else {
router.pushUrl({
url: 'pages/LoginPage',
params: {
hideJump: true
}
})
}
})
Row() {
Text('关于我们')
.fontColor(Color.Black)
.margin({ left: 15 })
Image($r("app.media.ic_mine_next"))
.width(25)
.height(22)
.margin({ right: 15 })
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height(50)
.margin({ top: 20 })
.borderRadius(10)
.backgroundColor('#66CCCCCC')
.onClick(() => {
router.pushUrl({ url: 'pages/mine/MineAboutPage' })
})
Row() {
Text('退出登录')
.fontColor(Color.Black)
.margin({ left: 15 })
Image($r("app.media.ic_mine_next"))
.width(25)
.height(22)
.margin({ right: 15 })
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height(50)
.margin({ top: 20 })
.borderRadius(10)
.backgroundColor('#66CCCCCC')
.visibility(this.isLogin ? Visibility.Visible : Visibility.None)
.onClick(() => {
this.dialogController.open()
})
}.id('columnSetting')
.padding({ left: 10, right: 10 })
.alignRules({
top: { anchor: 'imageBg', align: VerticalAlign.Bottom },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
}).width('100%')
}
.backgroundColor(Color.White)
.width('100%')
.height('100%')
}
2、退出登录弹窗
private dialogController = new CustomDialogController({
builder: LoginOutDialog({ onLoginOut: () => {
this.onLoginOut()
} }),
customStyle: true,
alignment: DialogAlignment.Center,
})
3、执行退出登录请求
private onLoginOut() {
HttpManager.getInstance()
.request<LoginOutBean>({
method: RequestMethod.GET,
url: 'https://www.wanandroid.com/user/logout/json' //wanAndroid的API:Banner
})
.then((result: LoginOutBean) => {
LogUtils.info(TAG, "result: " + JSON.stringify(result))
if (result.errorCode == 0) {
this.clearUserData()
//跳转到登录界面
router.replaceUrl({ url: 'pages/LoginPage' })
}
})
.catch((error) => {
LogUtils.info(TAG, "error: " + JSON.stringify(error))
})
}
4、清除用户数据
private clearUserData() {
AppStorage.Set(Constants.APPSTORAGE_USERNAME, '')
AppStorage.Set(Constants.APPSTORAGE_PASSWORD, '')
AppStorage.Set(Constants.APPSTORAGE_ISLOGIN, false)
}
我的收藏
1、获取收藏数据
private getCollectData() {
HttpManager.getInstance()
.request<CollectBean>({
method: RequestMethod.GET,
header: { "Cookie": `loginUserName=${this.userName}; token_pass=${this.token_pass}` },
url: `https://www.wanandroid.com/lg/collect/list/${this.pageNum}/json` //wanAndroid的API:积分排行
})
.then((result: CollectBean) => {
LogUtils.info(TAG, "getCollectData result: " + JSON.stringify(result))
if (this.isRefresh) {
this.controller.finishRefresh()
} else {
this.controller.finishLoadMore()
}
if (result.errorCode == 0) {
if (this.isRefresh) {
this.collectListData = result.data.datas
} else {
if (result.data.datas.length > 0) {
this.collectListData = this.collectListData.concat(result.data.datas)
} else {
promptAction.showToast({ message: '没有更多数据啦!' })
}
}
}
this.dialogController.close()
})
.catch((error) => {
LogUtils.info(TAG, "error: " + JSON.stringify(error))
if (this.isRefresh) {
this.controller.finishRefresh()
} else {
this.controller.finishLoadMore()
}
this.dialogController.close()
})
}
2、UI界面实现
build() {
Column() {
AppTitleBar({ title: "我的收藏" })
RefreshListView({
list: this.collectListData,
controller: this.controller,
isEnableLog: true,
paddingRefresh: { left: 10, right: 10, top: 5, bottom: 5 },
refreshLayout: (item: CollectItemBean, index: number): void => this.itemLayout(item, index),
onItemClick: (item: CollectItemBean, index: number) => {
LogUtils.info(TAG, "点击了:index: " + index + " item: " + item)
router.pushUrl({
url: 'pages/WebPage',
params: {
title: item.title,
uriLink: this.isValidUrl(item.link) ? item.link : 'https://www.wanandroid.com/' + item.link //规避部分情况下偶现link链接不完整
}
}, router.RouterMode.Single)
},
onRefresh: () => {
//下拉刷新
this.isRefresh = true
this.pageNum = 0
this.getCollectData()
},
onLoadMore: () => {
//上拉加载
this.isRefresh = false
this.pageNum++
this.getCollectData()
}
}).flexShrink(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
}
@Builder
itemLayout(item: CollectItemBean, index: number) {
RelativeContainer() {
//标题
Text(HtmlUtils.formatStr(item.title))
.fontColor('#333333')
.fontWeight(FontWeight.Bold)
.maxLines(2)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
.fontSize(20)
.id("textTitle")
.alignRules({
top: { anchor: 'textAuthor', align: VerticalAlign.Bottom },
left: { anchor: '__container__', align: HorizontalAlign.Start }
})
//更新时间
Text("作者:" + item.author /*+ "\t分类:" + item.chapterName*/ + "\t\t收藏时间:" + item.niceDate)
.fontColor('#666666')
.fontSize(14)
.id("textNiceDate")
.alignRules({
bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
left: { anchor: '__container__', align: HorizontalAlign.Start }
})
}
.width('100%')
.height(90)
.padding(10)
.borderRadius(10)
.backgroundColor(Color.White)
}
个人积分
1、获取积分数据
private getIntegralData() {
HttpManager.getInstance()
.request<IntegralBean>({
method: RequestMethod.GET,
header: { "Cookie": `loginUserName=${this.userName}; token_pass=${this.token_pass}` },
url: `https://www.wanandroid.com/lg/coin/userinfo/json` //wanAndroid的API:积分排行
})
.then((result: IntegralBean) => {
this.IntegralDataState = true
LogUtils.info(TAG, "getIntegralData result: " + JSON.stringify(result))
if (result.errorCode == 0) {
this.userIntegralData = result.data
}
LogUtils.info(TAG, "IntegralDataState: " + this.IntegralDataState + " LeaderboardDataState: " + this.LeaderboardDataState)
if (this.IntegralDataState && this.LeaderboardDataState) {
this.dialogController.close()
}
})
.catch((error) => {
this.IntegralDataState = true
LogUtils.info(TAG, "error: " + JSON.stringify(error))
LogUtils.info(TAG, "IntegralDataState: " + this.IntegralDataState + " LeaderboardDataState: " + this.LeaderboardDataState)
if (this.IntegralDataState && this.LeaderboardDataState) {
this.dialogController.close()
}
})
}
//积分排行
private getLeaderboardData() {
HttpManager.getInstance()
.request<LeaderboardBean>({
method: RequestMethod.GET,
header: {
"Content-Type": "application/json"
},
url: `https://www.wanandroid.com/coin/rank/1/json` //wanAndroid的API:积分排行
})
.then((result: LeaderboardBean) => {
this.LeaderboardDataState = true
LogUtils.info(TAG, "getLeaderboardData result: " + JSON.stringify(result))
if (result.errorCode == 0) {
this.leaderboardListData = result.data.datas
}
LogUtils.info(TAG, "IntegralDataState: " + this.IntegralDataState + " LeaderboardDataState: " + this.LeaderboardDataState)
if (this.IntegralDataState && this.LeaderboardDataState) {
this.dialogController.close()
}
})
.catch((error) => {
this.LeaderboardDataState = true
LogUtils.info(TAG, "error: " + JSON.stringify(error))
LogUtils.info(TAG, "IntegralDataState: " + this.IntegralDataState + " LeaderboardDataState: " + this.LeaderboardDataState)
if (this.IntegralDataState && this.LeaderboardDataState) {
this.dialogController.close()
}
})
}
2、UI界面实现
build() {
Column() {
AppTitleBar({ title: '个人积分' })
Row() {
Text('个人积分信息')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('积分获取列表 >>')
.fontSize(14)
.fontColor('#66666666')
.fontWeight(FontWeight.Bold)
.onClick(() => {
router.pushUrl({ url: 'pages/mine/PointAcquisitionPage' })
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.height(50)
.padding({ left: 15, right: 15 })
.backgroundColor('#66CCCCCC')
Stack() {
Column() {
Text(decodeURIComponent(this.userName))
.fontColor(Color.Black)
.fontSize(28)
.margin({ top: 5, bottom: 5 })
.fontWeight(FontWeight.Bold)
Text('积分:' + this.userIntegralData.coinCount)
.fontColor(Color.Red)
.margin({ top: 10, bottom: 10 })
.fontSize(20)
Text('等级:' + this.userIntegralData.level)
.fontColor(Color.Green)
.margin({ top: 10, bottom: 10 })
.fontSize(20)
Text('排名:' + this.userIntegralData.rank)
.fontColor('#1296db')
.margin({ top: 10, bottom: 10 })
.fontSize(20)
}
.alignItems(HorizontalAlign.Start)
.padding(10)
.borderRadius(10)
.backgroundColor('#F1F3F5')
.width('100%')
}.width('100%')
.padding({ left: 10, right: 10, top: 15, bottom: 15 })
//积分排行榜
Row() {
Text('积分排行榜')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('更多信息 >>')
.fontSize(14)
.fontColor('#66666666')
.fontWeight(FontWeight.Bold)
.onClick(() => {
router.pushUrl({ url: 'pages/mine/LeaderboardPage' })
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.height(50)
.padding({ left: 15, right: 15 })
.backgroundColor('#66CCCCCC')
if (this.leaderboardListData.length > 3) {
LeaderboardItemLayout({ item: this.leaderboardListData[0] }).padding(10)
LeaderboardItemLayout({ item: this.leaderboardListData[1] }).padding(10)
LeaderboardItemLayout({ item: this.leaderboardListData[2] }).padding(10)
}
}
.width('100%')
.height('100%')
}
3、效果
关于我们界面实现
1、效果
2、原理分析
要实现'关于我们'文字和背景图片随着手指滑动而变化的效果。监听最外层布局的滑动事件,然后动态设置绝对偏移量(通过position方法实现)。
图片设置绝对偏移量
Image($r('app.media.ic_background_mine'))
.width('100%')
.height(this.imageHeight)
.position({ x: 0, y: this.offsetY - this.pullMaxHeight })
'关于我们'文字根据滑动距离动态设置大小
Text('关于我们')
.fontColor(Color.White)
.fontSize(this.fontSize)
.position({ x: this.textInitialX, y: this.textInitialY + this.textPositionY })
底部内容设置绝对偏移量
Column() {
//......
}
.padding(10)
.position({ y: this.imageHeight + this.offsetY - this.pullMaxHeight })
3、详细代码
import LogUtils from '@app/BaseLibrary/src/main/ets/utils/LogUtils'
import router from '@ohos.router';
const TAG = 'MineAboutPage--- ';
/**
* 关于我们
*/
@Entry
@Component
struct MineAboutPage {
// 列表y坐标偏移量
@State offsetY: number = 0
// 按下的y坐标
private downY = 0
// 上一次手指抬起后的背景偏移量
private lastUpOffsetY = 0
//上一次手指抬起后的文字偏移量
private lastUpTextPositionY = 0
// 下拉的最大高度
private pullMaxHeight: number = 100
//收缩后title的高度
private pushMaxHeight: number = 56
//图片高度
private imageHeight: number = 300
//文字初始偏移量,x
private textInitialX: number = 60
//文字初始偏移量,y
private textInitialY: number = 60
//文字初始大小
private textFontSize: number = 40
@State fontSize: number = this.textFontSize
@State textPositionY: number = 0
@State pushStatus: boolean = false
build() {
Column() {
Stack() {
Image($r('app.media.ic_background_mine'))
.width('100%')
.height(this.imageHeight)
.position({ x: 0, y: this.offsetY - this.pullMaxHeight })
Image($r('app.media.ic_back_white'))
.width(26)
.height(26)
.margin({ left: 20, right: 20, top: 15 })
.onClick(() => {
router.back()
})
Text('关于我们')
.fontColor(Color.White)
.fontSize(this.fontSize)
.position({ x: this.textInitialX, y: this.textInitialY + this.textPositionY })
}
.alignContent(Alignment.TopStart)
.width('100%')
.height(this.imageHeight - this.pullMaxHeight)
.id('stackHeader')
.alignRules({
left: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top }
})
Column() {
Text("APP介绍:")
.fontColor(Color.Black)
.fontSize(25)
.fontWeight(FontWeight.Bold)
.alignSelf(ItemAlign.Start)
Text("\t这是一款鸿蒙应用,基于鸿洋大佬WanAndroid的API进行开发,目前主要功能包括:登录、注册,搜索,首页,导航,项目,个人信息等模块。")
.lineHeight(25)
.fontSize(20)
.fontColor(Color.Black)
.margin({ top: 10 })
Text() {
Span('\t可以在').fontSize(20)
Span('WanAndroid(鸿蒙)')
.decoration({ type: TextDecorationType.Underline, color: Color.Blue }).fontSize(18)
.onClick(() => {
router.pushUrl({
url: 'pages/WebPage',
params: {
title: 'WanAndroid(鸿蒙)',
uriLink: 'https://blog.csdn.net/abner_crazy/category_12565384.html',
isShowCollect: false,
}
}, router.RouterMode.Single)
})
.fontColor(Color.Blue)
Span('专栏中关注项目相关介绍,后续会在该专栏中更新项目开发相关知识点,欢迎大家在专栏下方留言进行沟通交流。')
.fontSize(20)
}
.lineHeight(25)
.margin({ top: 20 })
}
.padding(10)
.position({ y: this.imageHeight + this.offsetY - this.pullMaxHeight })
}
.onTouch((event) => this.touchEvent(event))
.width('100%')
.height('100%')
}
touchEvent(event: TouchEvent) {
switch (event.type) {
case TouchType.Down: // 手指按下
// 记录按下的y坐标
this.downY = event.touches[0].y
break
case TouchType.Move: // 手指移动
this.touchMovePull(event.touches[0].y)
break
case TouchType.Up: // 手指抬起
case TouchType.Cancel: // 触摸意外中断
if (this.offsetY > 0) {
this.pushStatus = false
this.setOriginalStatus()
} else { //手指抬起后在初始态上面,即上划
this.pushStatus = true
this.lastUpOffsetY = this.offsetY
this.lastUpTextPositionY = this.textPositionY
}
break
}
}
// 手指移动,处理下拉
touchMovePull(touchesY: number) {
//最大差值
let maxHeight = this.imageHeight - this.pullMaxHeight - this.pushMaxHeight
// 滑动的偏移量
let offset = touchesY - this.downY
// 偏移量的值缓慢增加
let height = offset * 0.50
if (this.pushStatus) {
this.offsetY = height + this.lastUpOffsetY > this.pullMaxHeight ? this.pullMaxHeight : height + this.lastUpOffsetY
} else {
this.offsetY = height > this.pullMaxHeight ? this.pullMaxHeight : height
}
LogUtils.info(TAG, " offsetY: " + this.offsetY + " height: " + height + " lastUpOffsetY: " + this.lastUpOffsetY)
//滑到title高度停止
if (this.offsetY < -maxHeight) {
this.offsetY = -maxHeight
}
//文字Y轴偏移量
if (this.pushStatus) {
this.textPositionY = this.lastUpTextPositionY + offset * 0.16 /*> 104 ? 104 : this.lastUpTextPositionY + offset * 0.16*/
} else {
this.textPositionY = offset * 0.16
}
if (this.textPositionY < -42) {
this.textPositionY = -42
}
if (this.textFontSize + this.textPositionY * 0.4762 <= this.textFontSize) {
this.fontSize = this.textFontSize + this.textPositionY * 0.4762
} else {
this.fontSize = this.textFontSize
}
LogUtils.info(TAG, "touchMovePull offsetY: " + this.offsetY + " textPositionY: "
+ this.textPositionY + " pushStatus: " + this.pushStatus + " fontSize: " + this.fontSize)
}
setOriginalStatus() {
animateTo({
duration: 300,
}, () => {
this.offsetY = 0
this.textPositionY = 0
})
}
}
总结:
到这篇文章为止整个WanAndroid(鸿蒙)版本的功能开发基本完成了,后续会继续更新一些优化点,大家可以关注后面更新内容~~