赞
踩
今天带来的实战案例是朋友圈,先看成品:
友情提示:本项目使用stage模型,ArkTs语言,api9。
先看项目结构,本项目所有代码都在Index.ets文件下,图片资源在image文件夹和resources下的media文件下都有分布。
这项目就一个界面,看似简单,其实一步一个难点,比上一篇文章的微信项目有过之无不及。主要难点有以下几个:
1、头像昵称的叠加布局
2、导航栏的隐藏和显示
3、评论弹窗的动画效果和单例显示
4、点击图片预览
在开始构建页面之前,先解决一下数据问题,我先定义一个数据模型,然后自己手写几条朋友圈数据:
- class MomentClass {
- public nickName: string; //昵称
- public content: string; //内容
- public date: string; //发表日期
- public showC: boolean; //是否显示评论和赞弹窗
- public images: string[]; //内容图片列表
-
- constructor(nickName: string,content: string,date: string,showC: boolean,images:string[]) {
- this.nickName = nickName;
- this.content = content;
- this.date = date;
- this.showC = showC;
- this.images = images;
- }
- }
- @State momentList: MomentClass[] = [
- new MomentClass('老张1','今朝有酒今朝醉','8天前',false,['/image/moment_1.png']),
- new MomentClass('老张2','hahahah2','8天前',false,['/image/grid_0.png','/image/grid_1.png','/image/grid_2.png','/image/grid_1.png']),
- new MomentClass('老张3','哈哈真的很不错','8天前',false,[]),
- new MomentClass('老张4','我emo了我emo了我emo了我emo了。。。。。。。。','8天前',false,[]),
- new MomentClass('老张5','今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉今朝有酒今朝醉','8天前',false,[]),
- ];
有了数据就可以开始开发页面了。我们先忽略导航栏,先看滚动区域部分,显然它是一个List,根据内容不同我把它分成两个ListItemGroup:
第1部分,叠加布局,背景图片上叠加昵称和头像,对齐格式设置bottomEnd,然后调整一下外边距margin就会有底部超出范围的效果:
- ListItemGroup(){
- ListItem(){
- Stack({alignContent:Alignment.BottomEnd}){
- Flex() {
- Image('/image/backImage.png')
- .width('100%')
- .height(300)
- }
-
- Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.End,alignItems:ItemAlign.Center}){
- Text("幽蓝君")
- .fontColor(Color.White)
- .margin({right:5})
- Image('/image/head.png')
- .width(70)
- .height(70)
- .borderRadius(8)
- }
- .padding({right:10})
- .width('100%')
- .height(70)
- .margin({bottom:-20})
- }
- .width('100%')
- .height(300)
- }
- }
下面的内容区域需要好好分析一下:
图中标出1、2、3部分相信对大家没什么难度,第4部分我使用的是Grid容器,然后根据图片的数量调整列数和行数:代码如下:
-
- Grid(){
- ForEach(this.momentItem.images, (imagePath: string,index:number) => {
- GridItem() {
- Image(imagePath)
- }
- })
- }
- .columnsTemplate(this.calcColumnsTemplate())
- .rowsTemplate(this.calcRowsTemplate())
- .width(this.calcGridRow())
- .height(this.calcGridColum())
- .columnsGap(7)
- .rowsGap(7)
- .margin({top:5})
- //计算列数
- calcRowsTemplate(){
- if(this.momentItem.images.length <= 3){
- return '1fr'
- }else if(this.momentItem.images.length > 3 && this.momentItem.images.length <= 6){
- return '1fr 1fr'
- }else {
- return '1fr 1fr 1fr'
- }
- }
- //计算行数
- calcColumnsTemplate(){
- if(this.momentItem.images.length <= 1){
- return '1fr'
- }else if(this.momentItem.images.length == 2){
- return '1fr 1fr'
- }else {
- return '1fr 1fr 1fr'
- }
- }
第5部分看似是横向布局,它其实是一个叠加布局,黑色弹窗和两侧内容的叠加。代码如下:
- Stack({alignContent:Alignment.End}){
- Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center}){
- Text(this.momentItem.date)
- .fontColor('rgb(185,185,185)')
- .fontSize(13)
- Text('••')
- .width(35)
- .height(18)
- .borderRadius(5)
- .fontSize(12)
- .fontWeight(900)
- .textAlign(TextAlign.Center)
- .fontColor('rgb(90,109,150)')
- .backgroundColor('rgb(247,247,247)')
-
- }
- .padding({right:20})
- .height(40)
-
-
- Flex({direction:FlexDirection.Row,}){
- Flex({direction:FlexDirection.Row}){
- Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.Center,alignItems:ItemAlign.Center}){
- Image($r('app.media.like'))
- .width(18)
- .height(18)
- Text('赞')
- .fontColor(Color.White)
- .fontSize(14)
- .textAlign(TextAlign.Center)
- }
- .width(60)
- .height(40)
- Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.Center,alignItems:ItemAlign.Center}){
- Image($r('app.media.commen'))
- .width(18)
- .height(18)
- Text('评论')
- .fontColor(Color.White)
- .fontSize(14)
- .textAlign(TextAlign.Center)
- }
- .width(60)
- .height(40)
- }
- .borderRadius(4)
- .width(120)
- .height(40)
- .position({x:0})
- .backgroundColor('rgb(22,22,22)')
-
- }
- .margin({right:60})
- .width(120)
- .height(40)
-
- }
- .height(40)
这样做完之后,看下效果:
问题来了,评论和赞的弹窗不应该每一行都显示,它只能点击的时候显示一个并且带有动画效果,所以现在全部把它们隐藏,注意这里的隐藏不能使用Visibility.Hidden,而是设置位置和尺寸。因为只有宽度和x坐标的同时变化才能做到朋友圈那样的效果,假设弹窗显示时的x坐标是0,宽度120,那么动画开始之前x初始值应该是120,宽度为0才对,但是幽蓝君亲身实验宽度为0时是不行的,所以这里宽度初始值设置0.01,肉眼看不见就好。
说了一通,不知道大家能不能理解,看代码吧,当右侧的按钮点击时:
- @State menuWidth:number = 0.001 //弹窗width初始值
- @State menuX:number = 120 //弹窗x值初始值
- animateTo({
- duration: 300,
- onFinish:() => {
- this.momentItem.showC = !this.momentItem.showC
- }
- }, () => {
- if(!this.momentItem.showC){
- //弹出
- this.menuX = 0
- this.menuWidth = 120
- }else {
- //收起
- this.menuX = 120
- this.menuWidth = 0.001
- }
- })
这样可以实现单个弹窗的弹出和收起,但是我们要控制整个页面只能出现一个弹窗,所以在上述点击事件中要同时把点击的序号index传给父组件,让父组件处理数据列表,除index外其他行的showC置为false。
问题又来了,我们之前说过,@state修饰的变量,嵌套数据的变化它是监测不到的,所以即便把数据修改了其他弹窗也不会隐藏。
所以子组件中数据对象使用@ObjectLink修饰,数据模型MomentClass也要使用@Observed修饰:
- @Observed
- class MomentClass {
- public nickName: string;
- public content: string;
- public date: string;
- public showC: boolean;
- public images: string[];
-
- constructor(nickName: string,content: string,date: string,showC: boolean,images:string[]) {
- this.nickName = nickName;
- this.content = content;
- this.date = date;
- this.showC = showC;
- this.images = images;
- }
- }
-
- @ObjectLink @Watch('onCountUpdated2') momentItem: MomentClass;
这样就完成了整个评论弹窗的控制。
接下来就是导航栏了,导航栏有一个显示和隐藏的切换,规律是当第一个listGroupItem滑动消失时导航栏显示,否则隐藏。List组件有一个onScrollIndex方法:
- onScrollIndex((start,end)=>{
- if(start == 0 && end == 1){
- this.navHide = true
- }
- if(start == 1 && end == 1){
- this.navHide = false
- }
- })
然后把navHide变量赋值给导航栏的hideTitleBar:
hideTitleBar(this.navHide)
这样导航栏就可以按照我们要求显示和隐藏了,至于隐藏时的导航栏图标大家可以自己添加一个,和导航栏反向显示就行了。
最后就是点击预览朋友圈图片,幽蓝君的做法是点击图片弹出一个List组件覆盖到屏幕上,看代码:
-
- if (this.previewAlpha) {
- List({initialIndex:this.bigImageIndex}){
- ForEach(this.bigImageList,(imagePath)=>{
- ListItem(){
- Image(imagePath)
- .width('100%')
- .height('100%')
- .objectFit(ImageFit.Auto)
- }
- })
-
- }
- .edgeEffect(EdgeEffect.Spring)
- .listDirection(Axis.Horizontal)
- .width('100%')
- .height('100%')
- .backgroundColor(Color.Black)
- .onClick(()=>{
- animateTo({ duration: 500 }, () => {
- // 动画闭包内控制Image组件的出现和消失
- this.previewAlpha = !this.previewAlpha;
- })
- })
- //弹出动画
- .transition({ type: TransitionType.Insert, translate: { x: 20, y: 20 },scale: { x: 0, y: 0 } })
- //消失动画
- .transition({ type: TransitionType.Delete, opacity: 0, scale: { x: 0, y: 0 } })
- }
点击图片的时候设置previewAlpha:
-
- animateTo({ duration: 500 }, () => {
- // 动画闭包内控制Image组件的出现和消失
- this.previewAlpha = !this.previewAlpha;
- })
这样朋友圈的功能就基本完成了。
大家如需获取本次内容源码,请点击尾部链接,直接获取
最后,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(Harmony NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(Harmony NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
HarmonOS基础技能
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
OpenHarmony北向、南向开发环境搭建
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。