这里我们将采用组件化的思想进行开发
拆解组件
- pages下,新建ZhiHu的文件
- pages下,新建components, 里面新建 HmNavBar和HmCommentItem
components/HmNavBar.ets
@Entry
@Component
struct HmNavBar {
title: string = '标题'
build() {
Row() {
// 返回键
Row() {
Image($r("app.media.left_arrow"))
.width(16)
.height(16)
}
.width(30)
.height(30)
.borderRadius(15)
.backgroundColor('#f4f4f4')
.justifyContent(FlexAlign.Center)
.margin({
left: 20
})
Text(this.title)
.layoutWeight(1)
.textAlign(TextAlign.Center)
.margin({
right: 50
})
}
.width("100%")
.height(50)
.border({
color: "#f4f5f6",
width: {
bottom: 1
}
})
}
}
export default HmNavBar
components/HmCommentItem.ets
@Entry
@Component
struct HmCommentItem {
build() {
Row({space: 10}) {
Image("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F1bad8264-7428-44cf-a92d-3016a2de537b%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1711626934&t=5478cb3adef5d3e29e6952934797ca39")
.width(40)
.height(40)
.borderRadius(20)
Column() {
Text('周杰伦')
.fontSize(18)
.fontColor('#303a43')
.fontWeight(FontWeight.Bold)
Text('黄河江最近一代都带蓝牙,意大利拌面必须使用42👌钢筋混凝土量子力学')
.fontColor("#2f3642")
.lineHeight(22)
Row() {
Text('10-21 .IP属地北京')
.fontColor("#cacaca")
.fontSize(12)
Row({ space: 4 }) {
Image($r("app.media.ic_public_like"))
.width(12)
.height(12)
.fillColor("#cacaca")
Text("100")
.fontColor("#cacaca")
.fontSize(12)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.alignItems(VerticalAlign.Top)
.padding(20)
.width('100%')
}
}
export default HmCommentItem;
使用
pages/ZhiHu.ets
import HmCommentItem from './components/HmCommentItem'
import HmNavBar from './components/HmNavBar'
@Entry
@Component
struct ZhiHu {
build() {
Column() {
HmNavBar({
title: '评论回复'
})
HmCommentItem()
Divider().strokeWidth(6)
Row() {
Text("评论数50")
}
.width('100%')
.height(50)
.padding({
left: 20
})
.border({
color: '#f3f4f5',
width: {
bottom: 1
}
})
ForEach([1,2,3,4], () => {
HmCommentItem()
})
}
}
}
需要出现滚动区域。
使用了List组件,子组件必须有ListItem/ListItemGroup
List() {
ForEach([1,2,3,4,5,6], () => {
ListItem() {
HmCommentItem()
}
})
}.layoutWeight(1)
评论列表
在 pages 下新建 models 文件夹
models/index.ets
- 定义一个评论的interface
export interface ReplyItem {
avatar: ResourceStr // 头像
author: string // 作者
id: number // 评论的id
content: string // 评论内容
time: string // 发表时间
area: string // 地区
likeNum: number // 点赞数量
likeFlag: boolean | null // 当前用户是否点过赞
}
安装12c
npm i -g interface2class
检测是否安装成功:i2c -V
执行带有interface的文件
cmd进入到当前文件所在目录,然后输入 ic2 ./Index.ets
执行
用i2c生成对应的class
上面我们cmd进入到models目录后,运行ic2 ./index.ets
,便会在models文件夹下的index.ets文件里为我们新增以下代码
export class ReplyItemModel implements ReplyItem {
avatar: ResourceStr = ''
author: string = ''
id: number = 0
content: string = ''
time: string = ''
area: string = ''
likeNum: number = 0
likeFlag: boolean | null = null
constructor(model: ReplyItem) {
this.avatar = model.avatar
this.author = model.author
this.id = model.id
this.content = model.content
this.time = model.time
this.area = model.area
this.likeNum = model.likeNum
this.likeFlag = model.likeFlag
}
}
- 定义一个评论列表数据
因为我们需要的是class对象,所以每个对象都需要new一下
pages/ZhiHu.ets
import HmCommentItem from './components/HmCommentItem'
import HmNavBar from './components/HmNavBar'
import { ReplyItemModel } from './models'
@Entry
@Component
struct ZhiHu {
@State commentList: ReplyItemModel[] = [
new ReplyItemModel({
id: 1,
avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
author: '偏执狂-妄想家',
content: '更何况还分到一个摩洛哥[惊喜]',
time: '11-30',
area: '海南',
likeNum: 34,
likeFlag: false
}) ,
new ReplyItemModel({
id: 2,
avatar: 'https://pic1.zhimg.com/v2-5a3f5190369ae59c12bee33abfe0c5cc_xl.jpg?source=32738c0c',
author: 'William',
content: '当年希腊可是把1:0发挥到极致了',
time: '11-29',
area: '北京',
likeNum: 58,
likeFlag: false
}),
new ReplyItemModel({
id: 3,
avatar: 'https://picx.zhimg.com/v2-e6f4605c16e4378572a96dad7eaaf2b0_l.jpg?source=06d4cd63',
author: 'Andy Garcia',
content: '欧洲杯其实16队球队打正赛已经差不多,24队打正赛意味着正赛阶段在小组赛一样有弱队。',
time: '11-28',
area: '上海',
likeNum: 10,
likeFlag: false
}),
new ReplyItemModel({
id: 4,
avatar: 'https://picx.zhimg.com/v2-53e7cf84228e26f419d924c2bf8d5d70_l.jpg?source=06d4cd63',
author: '正宗好鱼头',
content: '确实眼红啊,亚洲就没这种球队,让中国队刷',
time: '11-27',
area: '香港',
likeNum: 139,
likeFlag: false
}),
new ReplyItemModel({
id: 5,
avatar: 'https://pic1.zhimg.com/v2-eeddfaae049df2a407ff37540894c8ce_l.jpg?source=06d4cd63',
author: '柱子哥',
content: '我是支持扩大的,亚洲杯欧洲杯扩到32队,世界杯扩到64队才是好的,世界上有超过200支队伍,欧洲区55支队伍,亚洲区47支队伍,即使如此也就六成出现率',
time: '11-27',
area: '旧金山',
likeNum: 29,
likeFlag: false
}),
new ReplyItemModel({
id: 6,
avatar: 'https://picx.zhimg.com/v2-fab3da929232ae911e92bf8137d11f3a_l.jpg?source=06d4cd63',
author: '飞轩逸',
content: '禁止欧洲杯扩军之前,应该先禁止世界杯扩军,或者至少把亚洲名额一半给欧洲。',
time: '11-26',
area: '里约',
likeNum: 100,
likeFlag: false
})
]
build() {
Column() {
HmNavBar({
title: '评论回复'
})
HmCommentItem({
item: new ReplyItemModel({
id: 999,
author: '周杰伦',
avatar: $r("app.media.startIcon"),
likeNum: 10,
likeFlag: false,
time: '03-02',
area: '北京',
content: '人到了一定的年龄新陈代谢就慢了,吃了胖不吃瘦了皱纹就多,要靠锻炼 '
})
})
Divider().strokeWidth(6)
Row() {
Text("评论数50")
}
.width('100%')
.height(50)
.padding({
left: 20
})
.border({
color: '#f3f4f5',
width: {
bottom: 1
}
})
List() {
ForEach(this.commentList, (item: ReplyItemModel) => {
ListItem() {
HmCommentItem({ item })
}
})
}.layoutWeight(1)
}
}
components/HmCommentItem.ets
import { ReplyItem, ReplyItemModel } from '../models';
@Entry
@Component
struct HmCommentItem {
// 接收渲染的选项
item: ReplyItemModel = new ReplyItemModel({} as ReplyItem) // 初始值 只是为了语法不报错
build() {
Row({space: 10}) {
Image(this.item.avatar)
.width(40)
.height(40)
.borderRadius(20)
Column() {
Text(this.item.author)
.fontSize(18)
.fontColor('#303a43')
.fontWeight(FontWeight.Bold)
Text(this.item.content)
.fontColor("#2f3642")
.lineHeight(22)
Row() {
Text(`${this.item.time} .IP属地${this.item.area}`)
.fontColor("#cacaca")
.fontSize(12)
Row({ space: 4 }) {
Image($r("app.media.ic_public_like"))
.width(12)
.height(12)
.fillColor("#cacaca")
Text("100")
.fontColor("#cacaca")
.fontSize(12)
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.alignItems(VerticalAlign.Top)
.padding(20)
.width('100%')
}
}
export default HmCommentItem;
底部回复按钮
components/HmReplyInput.ets
@Component
struct HmReplyInput {
@State
content: string = ""
build() {
Row({ space: 10 }) {
TextInput({ text: $$this.content, placeholder: '~请留下您的神评论' })
.layoutWeight(1)
.height(40)
Button("发布")
}
.padding({ left: 10, right: 10 })
.width('100%')
.height(60)
}
}
export { HmReplyInput }
pages/ZhiHu.ets
import HmCommentItem from './components/HmCommentItem'
import HmNavBar from './components/HmNavBar'
import { HmReplyInput } from './components/HmReplyInput'
import { ReplyItemModel } from './models'
@Entry
@Component
struct ZhiHu {
@State commentList: ReplyItemModel[] = [
...
]
build() {
Column() {
HmNavBar({
title: '评论回复'
})
HmCommentItem({
...
})
Divider().strokeWidth(6)
Row() {
Text("评论数50")
}
.width('100%')
.height(50)
.padding({
left: 20
})
.border({
color: '#f3f4f5',
width: {
bottom: 1
}
})
List() {
...
}.layoutWeight(1)
++ HmReplyInput()
}
}
}
实现评论点赞
涉及知识点:子组件如何调用父组件的函数
- 子组件要声明一个函数
test: () => void = () => {} // test:变量类型 = 初始值
- 父组件需要给子组件传入这个参数
Child({
test: () => {
this.abc()
}
})
实现点赞
components/HmCommentItem.ets
import { ReplyItem, ReplyItemModel } from '../models';
@Entry
@Component
struct HmCommentItem {
// 接收渲染的选项
item: ReplyItemModel = new ReplyItemModel({} as ReplyItem) // 初始值 只是为了语法不报错
// 点赞
++ changeLike: () => void = () => {}
build() {
Row({space: 10}) {
Image(this.item.avatar)
.width(40)
.height(40)
.borderRadius(20)
Column() {
...
Row() {
Text(`${this.item.time} .IP属地${this.item.area}`)
.fontColor("#cacaca")
.fontSize(12)
Row({ space: 4 }) {
Image($r("app.media.ic_public_like"))
.width(12)
.height(12)
.fillColor(this.item.likeFlag ? Color.Red : "#cacaca")
Text(this.item.likeNum.toString())
.fontColor(this.item.likeFlag ? Color.Red :"#cacaca")
.fontSize(12)
}
.onClick(() => {
this.changeLike()
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.alignItems(VerticalAlign.Top)
.padding(20)
.width('100%')
}
}
export default HmCommentItem;
pages/ZhiHu.ets
changeLike (item: ReplyItemModel) {
// 需要拿到点击的数据 拿到数据更新数据即可
// item.likeNum
// item.likeFlag
if(item.likeFlag) {
// 点过赞
item.likeNum--
}
else {
// 没有点过赞
item.likeNum++
}
item.likeFlag = !item.likeFlag // 取反
// State的修饰符的更新机制
// 只能监测到第一层
const index = this.commentList.findIndex(obj => obj.id === item.id)
//this.commentList[index] = item // 这么写为什么不行 // 引用类型 基础类型
this.commentList[index] = new ReplyItemModel(item)
}
List() {
ForEach(this.commentList, (item: ReplyItemModel) => {
ListItem() {
HmCommentItem({ item,changeLike: () => {
this.changeLike(item)
} })
}
})
}.layoutWeight(1)
更新的秘密
- 鸿蒙里面的所有的更新都只能监测到一层的更新
- 如果要更新数组里面的某一项的话
- this.list[index] = 新值
- this.list.splice(index, 1, 新值)
- 关于key的秘密
尝试给了一个id作为key,为什么没有更新
因为鸿蒙会根据key的不同来更新的内容,如果key前后一样,它认为你没有变,那就不更新
顶部的点赞
pages/ZhiHu.ets
import HmCommentItem from './components/HmCommentItem'
import HmNavBar from './components/HmNavBar'
import { HmReplyInput } from './components/HmReplyInput'
import { ReplyItemModel } from './models'
@Entry
@Component
struct ZhiHu {
@State commentList: ReplyItemModel[] = [
...
]
++ @State
showTop: boolean = true
++ @State
currentComment: ReplyItemModel = new ReplyItemModel({
id: 999,
author: '周杰伦',
avatar: 'https://picx.zhimg.com/027729d02bdf060e24973c3726fea9da_l.jpg?source=06d4cd63',
likeNum: 10,
likeFlag: false,
time: '03-02',
area: '北京',
content: '人到了一定的年龄新陈代谢就慢了,吃了胖不吃瘦了皱纹就多,要靠锻炼 '
})
++ changeLike (item: ReplyItemModel, type?: "top" | "bottom") {
// 需要拿到点击的数据 拿到数据更新数据即可
// item.likeNum
// item.likeFlag
if(item.likeFlag) {
// 点过赞
item.likeNum--
}
else {
// 没有点过赞
item.likeNum++
}
item.likeFlag = !item.likeFlag // 取反
// promptAction.showToast({ message: JSON.stringify(item), duration: 300000 })
if(type !== "top") {
// State的修饰符的更新机制
// 只能监测到第一层
const index = this.commentList.findIndex(obj => obj.id === item.id)
//this.commentList[index] = item // 这么写为什么不行 // 引用类型 基础类型
// this.commentList[index] = new ReplyItemModel(item)
this.commentList.splice(index, 1, new ReplyItemModel(item))
}
}
build() {
Column() {
HmNavBar({
title: '评论回复'
})
// HmCommentItem({
// item: new ReplyItemModel({
// id: 999,
// author: '周杰伦',
// avatar: $r("app.media.startIcon"),
// likeNum: 10,
// likeFlag: false,
// time: '03-02',
// area: '北京',
// content: '人到了一定的年龄新陈代谢就慢了,吃了胖不吃瘦了皱纹就多,要靠锻炼 '
// })
// })
++ if(this.showTop) {
HmCommentItem({
item: this.currentComment,
changeLike: () => {
this.changeLike(this.currentComment, "top")
this.showTop = false
setTimeout(() => {
this.showTop = true
}, 100)
}
})
}
Divider().strokeWidth(6)
Row() {
Text("评论数50")
}
.width('100%')
.height(50)
.padding({
left: 20
})
.border({
color: '#f3f4f5',
width: {
bottom: 1
}
})
List() {
ForEach(this.commentList, (item: ReplyItemModel) => {
ListItem() {
HmCommentItem({ item,changeLike: () => {
this.changeLike(item)
} })
}
})
}.layoutWeight(1)
HmReplyInput()
}
}
}
回复评论
- 底部输入组件双向绑定
components/HmReplyInput.ets
@Component
struct HmReplyInput {
@State
content: string = ""
publishComment: (content: string) => void = () => {}
build() {
Row({ space: 10 }) {
TextInput({ text: $$this.content, placeholder: '~请留下您的神评论' })
.layoutWeight(1)
.height(40)
.onSubmit(() => {
// 键盘的确定事件
if(this.content) {
this.publishComment(this.content)
this.content = ""
}
})
Button("发布")
.onClick(() => {
if(this.content) {
this.publishComment(this.content)
this.content = ""
}
})
}
.padding({ left: 10, right: 10 })
.width('100%')
.height(60)
}
}
export { HmReplyInput }
- 调用父组件传入的publishComment的方法
- 父组件实现的方法
pages/ZhiHu.ets
//创建scroller
scroller:Scroller = new Scroller()
// 新增评论
addComment(content: string) {
this.commentList.unshift(new ReplyItemModel({
id: Math.random() ,
avatar: 'https://foruda.gitee.com/avatar/1705232317138324256/1759638_itcast_panpu_1705232317.png',
author: '梧桐',
content,
time: `${(new Date().getMonth() + 1).toString().padStart(2, "0")}-${new Date().getDate().toString().padStart(2, "0")}`,
area: '北京',
likeNum: 0,
likeFlag: false
}))
// 控制滚动条
this.scroller.scrollEdge(Edge.Top)
}
// 创建scroller
List({ scroller: this.scroller }) {
ForEach(this.commentList, (item: ReplyItemModel) => {
ListItem() {
HmCommentItem({ item,changeLike: () => {
this.changeLike(item)
} })
}
})
}.layoutWeight(1)
HmReplyInput({
publishComment: (content: string) => {
this.addComment(content)
}
})