鸿蒙--知乎评论

这里我们将采用组件化的思想进行开发

拆解组件

  • 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)
  }
})
相关推荐
楚疏笃19 分钟前
鸿蒙学习生态应用开发能力全景图-开发者支持平台(5)
学习·华为·harmonyos
鸿蒙程序媛29 分钟前
【鸿蒙开发】第五章 ArkTS语言UI范式-UI相关的装饰器
harmonyos
亦世凡华、8 小时前
【HarmonyOS】鸿蒙系统在租房项目中的项目实战(一)
经验分享·harmonyos·harmonyos next·arkui·鸿蒙开发
Swift社区11 小时前
如何构建安全可靠的 HarmonyOS 应用
harmonyos·arkts·arkui
AI+程序员在路上11 小时前
鸿蒙系统(HarmonyOS)介绍
华为·harmonyos
lqj_本人11 小时前
鸿蒙next版开发:相机开发-录像(ArkTS)
数码相机·华为·harmonyos
Swift社区14 小时前
HarmonyOS 如何获取设备信息(系统、版本、网络连接状态)
华为·harmonyos
lqj_本人20 小时前
鸿蒙next版开发:相机开发-拍照(ArkTS)
数码相机·华为·harmonyos
郝晨妤20 小时前
HarmonyOS和OpenHarmony区别是什么?鸿蒙和安卓IOS的区别是什么?
android·ios·harmonyos·鸿蒙
lqj_本人1 天前
鸿蒙next版开发:相机开发-会话管理(ArkTS)
华为·harmonyos