门户网站搭建方案,wordpress主题 机械,网站建设流程及细节,网页代做价格目录 前言
功能展示
整体页面布局
最新和最热
写评论
点赞功能
界面构建 初始数据的准备 列表项部分的渲染
底部区域
index部分 知识点概述
List组件
List组件简介
ListItem组件详解
ListItemGroup组件介绍
ForEach循环渲染
列表分割线设置
列表排列方向设…
目录 前言
功能展示
整体页面布局
最新和最热
写评论
点赞功能
界面构建 初始数据的准备 列表项部分的渲染
底部区域
index部分 知识点概述
List组件
List组件简介
ListItem组件详解
ListItemGroup组件介绍
ForEach循环渲染
列表分割线设置
列表排列方向设置
索引值计算规则
Prop装饰器
Prop注解的基本作用
Prop注解的限制条件
Prop注解的使用规则
Prop注解的具体使用场景
全套代码
BottomCom部分
InfoCom部分
InfoItem部分
CommentDate部分
Index部分 个人主页→VON 收录专栏→鸿蒙开发小型案例总结 基础语法部分会发布于github 和 gitee上面暂未发布 由于一些个人的特殊原因基础部分的代码迟迟未进行上传我会尽快进行整理发布后会第一时间进行通知希望大家多多谅解 前言 鸿蒙基础部分到这里也要和大家说再见了此案例所用到的知识点众多构建过程较为复杂。希望读者们能够认真观看。我也会尽力帮助大家梳理代码以及各各部分的逻辑及其思路。 此案例可以应用于多个地方比如博客短视频app等都可以进行应用
功能展示
整体页面布局 整体分为三大部分分别为头部、中间、底部。认真观察不难发现头部区域采用了Row中间区域采用了List底部区域采用了两个Row。整体布局为层叠布局。 最新和最热 最新和最热点击时显示的效果不同并且列表所展示的也不同。点击最新时评论的内容会根据时间进行排序最热时会根据点赞数进行排序。 写评论 自己可以在写评论处发布评论列表会根据评论进行渲染。 点赞功能 界面构建
由于代码的长度限制过于基础的部分不在进行逐一讲解希望大家谅解 最新和最热是两个按钮组件Extend装饰器进行修改当未被选中时是一种状态被选中时是另外一种状态。这两种状态的样式我是根据最左侧的颜色来进行适当调整的大家也可以效仿。 初始数据的准备 初始数据是自己进行构建的一些数据这些数据被打包在CommentDate中其中点赞数和等级是随机的其他部分都是自定义的。因为这是一个离线的静态页面并没有实现交互功能所以目前只能这样来代替。 列表项部分的渲染 渲染部分用到了CommentDate中的数据来进行逐一渲染的因为实现交互的是子组件但是要修改的部分是父组件的一些内容所以对于可变的数据用到了Prop装饰器进行的装饰。 底部区域 底部区域实现了双向绑定当输入评论并且单击回车键后数据会进行增加。并且会排在首位也就是下标为0的位置增添了一个全新的数据。 index部分 一些函数的定义及其功能的实现全都在index页面来进行实现的所以这一界面要尽量保持简介每一部分都可以进行抽取单独进行ui界面的构建通过import来进行导入即可。 知识点概述
List组件
鸿蒙开发中的List组件是一个功能强大且常用的UI组件用于呈现连续的数据列表。
List组件简介
基础定义List组件是一种容器组件用于展示一系列相同宽度的列表项适合连续、多行呈现同类数据例如图片和文本。参数配置List组件接受三个主要参数space子组件主轴方向的间隔、initialIndex初次加载时视口起始位置显示的item索引、scroller可滚动组件的控制器。
ListItem组件详解
基本概念ListItem是具体的列表项组件必须配合List使用用于展示每个具体的数据项。参数设置主要参数包括selectable是否可被鼠标框选和swipeAction设置划出组件的属性。
ListItemGroup组件介绍
功能描述用于展示列表项分组宽度默认充满List组件也必须配合List使用。主要参数header头部组件、footer尾部组件和space列表项间距。
ForEach循环渲染
应用场景当列表由多个相似或重复的列表项组成时为减少代码冗余可以使用ForEach进行循环渲染。工作原理ForEach接口基于数组类型数据进行循环渲染需与容器组件配合使用如List。
列表分割线设置
功能解释通过divider属性设置列表项之间的分割线样式提升视觉上的区分度和美观性。参数说明主要参数包括strokeWidth分割线的线宽、color分割线的颜色、startMargin和endMargin分割线距离列表侧边起始端和结束端的距离。
列表排列方向设置
垂直排列List默认采用垂直排列方式即列表项按垂直方向线性排列。水平排列通过设置listDirection属性为Axis.Horizontal可以实现列表项的水平排列。
索引值计算规则
规则概述索引值用来确定列表项在列表中的具体位置初次加载时默认从0开始。细节掌握初始索引可以通过initialIndex参数手动设置但需确保索引值不超过列表项总数。
Prop装饰器
Prop是一个方便的注解用于在鸿蒙应用开发中实现组件之间的数据传递。
在现代软件开发中组件化和数据传递是提高开发效率和代码可维护性的关键环节。特别是在鸿蒙这类分布式操作系统中对组件间数据传递机制的优化尤为重要。下面将详细探讨Prop注解的作用、限制条件、使用规则以及具体的使用场景。
Prop注解的基本作用
单向数据同步Prop注解主要用于实现组件间的单向数据同步。这意味着当父组件的状态发生改变时这些改变会通过Prop注解传递给子组件但子组件对这些属性的修改不会影响到父组件。支持的数据类型Prop能够处理各种基本数据类型包括字符串、数字、布尔值和枚举类型。这保证了其在不同场景下的灵活性和适用性。
Prop注解的限制条件
复杂类型的深拷贝当Prop涉及到复杂数据类型时如对象或数组会进行深拷贝操作。这一过程中除了基本类型如字符串、数字、Map、Set、Date和Array外其他类型可能会丢失。使用场景限制Prop不能在Entry装饰的自定义组件中使用。这限定了Prop的使用范围通常仅适用于页面级组件。
Prop注解的使用规则
参数配置Prop不需要特定参数其同步类型为单向同步。类型要求严格使用Prop时必须明确指定被装饰变量的具体类型不允许使用any类型也不允许使用undefined和null作为默认值。
Prop注解的具体使用场景
简单数据类型同步例如父组件中的State状态可以通过Prop注解传递给子组件。如果父组件的状态发生更新子组件的Prop也会相应更新。但如果子组件尝试修改这些属性更改不会反映到父组件中。数组项同步当父组件中的State数组项更新时子组件中对应的Prop也会同步更新。例如父组件可以包含一个数字数组每个元素用来初始化子组件中的一个Prop。同样子组件对Prop的修改不会反映到父组件中。类对象属性同步父组件中的State类对象可以用来初始化子组件的Prop。任何父组件中对象属性的更新都会同步到子组件但子组件对Prop的修改依旧不会反向同步到父组件。
全套代码 BottomCom部分
Component
struct BottomCom {State txt:stringonSubmitComment(content:string){}build() {Row(){Row(){Image($r(app.media.edit)).width(20).margin({left:10})TextInput({placeholder:写评论...,// 双向绑定text:$$this.txt}).backgroundColor(Color.Transparent).fontSize(18)// 回车.onSubmit((){this.onSubmitComment(this.txt)})}.height(40).backgroundColor(#f5f6f5).borderRadius(20).margin({left:15,right:20,top:10,bottom:10}).layoutWeight(1)Image($r(app.media.love_stare)).width(25)Image($r(app.media.like_stare)).width(25).margin({left:15,right:10})}.width(100%).height(60)}
}export default BottomCom
InfoCom部分
import BottomCom from ./BottomCom
Extend(Button)
function ButStyle(click:boolean){.fontSize(12).border({width:1,color:click?#fff:#ffbeb7b7}).width(46).height(32).padding({left:5,right:5}).fontColor(click ? #80555858 :#ff1f1e1e).backgroundColor(click ? #fff : #1ae0e0e0)
}
Component
struct InfoCom {State click:booleantrueonSort(type:number){}build() {Row(){Text(全部评论).fontSize(20).fontWeight(FontWeight.Bold)Row(){Button(最新).ButStyle(!this.click).onClick((){this.clicktruethis.onSort(0)})Button(最热).ButStyle(this.click).onClick((){this.clickfalsethis.onSort(1)})}}.justifyContent(FlexAlign.SpaceBetween).padding({left:20,right:20}).width(100%).height(60)}
}
export default InfoCom
InfoItem部分
import { CommentDate } from ../model/CommentDateComponent
struct InfoItem {Prop itemObj:CommentDateProp index:numberonLikeClick(index:number){}build() {// 列表项组件Column(){Row(){// 头像Image(this.itemObj.avatar).width(30).borderRadius(15).margin({top:10,right:5})// 昵称Text(this.itemObj.name).fontColor(#808d8585).fontSize(14).margin({top:10,left:5})// 等级Image(this.itemObj.levelIcon).width(20).margin({left:10,top:10})}// 评论内容Text(this.itemObj.commentext).fontSize(13).fontWeight(700).margin({top:10,left:40})Row(){// 时间Text(this.itemObj.timeString).fontSize(10).fontColor(Color.Gray)// 点赞Row(){Image(this.itemObj.islike ? $r(app.media.like_end) : $r(app.media.like_stare)).width(12)Text(this.itemObj.likenum.toString()).fontSize(10).fontColor(this.itemObj.islike ? Color.Red : Color.Gray)}.onClick((){this.onLikeClick(this.index)})}.width(100%).padding({left:40,right:20,top:15}).justifyContent(FlexAlign.SpaceBetween)}.alignItems(HorizontalAlign.Start).padding({left:15,top:10})}
}export default InfoItem
CommentDate部分
// 准备评论数据类
export class CommentDate{avatar:ResourceStr;// 头像name:string;// 昵称level:number;// 用户等级likenum:number;// 点赞数量commentext:string;// 评论内容islike:boolean;// 是否喜欢levelIcon:Resource;// level等级timeString:string;// 发布时间time:number // 时间戳constructor(avatar: ResourceStr, name: string, level: number, likenum: number, commentext: string,islike: boolean,time:number) {this.avataravatarthis.namenamethis.levellevelthis.likenumlikenumthis.commentextcommentextthis.islikeislikethis.timetimethis.levelIconthis.convertLevel(this.level)this.timeStringthis.convertTime(time)}//时间转换函数convertTime(time:number){const currentTimestamp new Date().getTime();// 转换为秒const timeDifference (currentTimestamp-time)/1000;console.log(timeDifference.toString())if(timeDifference0 || timeDifference0){return 刚刚;}else if(timeDifference60){return ${Math.floor(timeDifference)}秒前;}else if(timeDifference3600){return ${Math.floor(timeDifference/60)}分钟前;}else if(timeDifference86400){return ${Math.floor(timeDifference/3600)}小时前;}else if(timeDifference604800){return ${Math.floor(timeDifference/86400)}天前;}else if(timeDifference2592000){return ${Math.floor(timeDifference/604800)}周前;}else if(timeDifference31536000){return ${Math.floor(timeDifference/2592000)}个月前;}else{return ${Math.floor(timeDifference/31536000)}年前;}}// 等级图片获取convertLevel(Level:number){const iconList[$r(app.media.lv1),$r(app.media.lv2),$r(app.media.lv3),$r(app.media.lv4),$r(app.media.lv5),$r(app.media.lv6)]return iconList[Level]}
}// 封装一个方法创建假数据
export const createListRange():CommentDate[]{let result:CommentDate[]new Array()result[new CommentDate($r(app.media.tx_01),JohnYan,Math.floor(Math.random()*6),Math.floor(Math.random()*100),要是那天我抓住你就好了,false,1705850201128),new CommentDate($r(app.media.tx_03),cv工程师,Math.floor(Math.random()*6),Math.floor(Math.random()*100),故事不长也不难讲相识一场爱而不得,false,1643800201128),new CommentDate($r(app.media.tx_04),风行水上,Math.floor(Math.random()*6),Math.floor(Math.random()*100),后来啊书没有读好喜欢的人也没有在一起,false,1715850201128),new CommentDate($r(app.media.tx_05),枫以,Math.floor(Math.random()*6),Math.floor(Math.random()*100),你根本忘不了一个认认真真爱过的人你以为错过的是一个人其实你错过的是一整个人生,false,1680850201128),new CommentDate($r(app.media.tx_06),幼稚园里的幼稚鬼,Math.floor(Math.random()*6),Math.floor(Math.random()*100),有些伤痛即使已经痊愈也会留下难以愈合的伤疤,false,1705850201128),new CommentDate($r(app.media.tx_07),浮临子,Math.floor(Math.random()*6),Math.floor(Math.random()*100),不是所有的梦想都会成真不是所有的伤痛都能愈合但我们要有勇气继续前行,false,1625050201128),new CommentDate($r(app.media.tx_08),╭⌒浅浅笑,Math.floor(Math.random()*6),Math.floor(Math.random()*100),孤独并不可怕可怕的是渴望有人陪伴而得不到,false,1720850201128),new CommentDate($r(app.media.tx_09),枕头说它不想醒,Math.floor(Math.random()*6),Math.floor(Math.random()*100),可惜爱不是写诗 我只能欲言又止,false,1745050201128)]return result
}
Index部分
import InfoCom from ../components/InfoCom
import BottomCom from ../components/BottomCom
import InfoItem from ../components/InfoItem
import {CommentDate,createListRange} from ../model/CommentDateEntry
Component
struct Index {// 处理点赞时的方法handlelike(index:number){// 要有唯一标识// AlertDialog.show({// message:index.toString()// })// 父组件的方法如果抽取出来如果直接传递给子组件会有this指向问题this通常直接指向调用者// 需要用箭头函数包一层保证this还是指向父组件// 根据index进行判断let itemDatathis.commentList[index]if(itemData.islike){itemData.likenum-1}else{itemData.likenum1}itemData.islike !itemData.islike// 对于复杂类型状态对象状态数组只会对第一层数据进行监视变化this.commentList.splice(index,1,itemData)}// 处理提交handleSubmit(content:string){// 将数据添加到数组最前面const newItem:CommentDatenew CommentDate($r(app.media.tx_01),我,2,0,content,false,new Date().getTime())this.commentList[newItem,...this.commentList]}// 处理排序handleSort(type:number){if(type0){this.commentList.sort((a,b){return b.time-a.time})}else if(type1){this.commentList.sort((a,b){return b.likenum-a.likenum})}}// 初始化数据State commentList:CommentDate[]createListRange()// 生命周期函数会自动执行aboutToAppear(): void {this.handleSort(0)}build() {Column(){//头部InfoCom({onSort:(type:number){this.handleSort(type)}})//中间List(){ForEach(this.commentList,(item:CommentDate,index:number){ListItem(){// 列表项组件InfoItem({index:index,itemObj:item,onLikeClick:(index:number){// 此处的this就是父组件this.handlelike(index)}})}})}.width(100%).layoutWeight(1)//底部BottomCom({onSubmitComment:(content:string){this.handleSubmit(content)}})}.width(100%).height(100%)}
}