Harmony学习第四课(组件封装、循环渲染、事件传递)

image.png

先看效果

2024-03-06 13.53.13.gif

知识点

组件封装

// 使用@Component修饰某一个组件,在其中实现`build`方法即可
@Component
struct BannerView {
  @State content: string = ""  // 定义变量,可以是初始化的时候传入对应的值
  @State item: Partial<ReplyItem> = {} // 也可以传入对象
  onClicked: (item: ReplyItem) => void = () => { // 定义一个函数来接受传入的函数,在自定义类中调用的时候可以回传事件给父类
  }
  build() {
  }
}

初始化封装的组件

  headerReply: ReplyItem = {}
  onClicked(item: ReplyItem) {
    // 当子类调用onClicked的时候会父类会执行此代码块
  }
// 初始化的时候可以传入对应的值
  BannerView({content:"文字内容",item:this.headerReply, onClicked: (item: ReplyItem) => {
      this.onClicked(item)
  } })

!!!特别说明

BannerView({content:"文字内容",item:this.headerReply, onClicked: this.onClicked(item))

这是错误的写法,此时this指向的是子类,而非父类。

循环渲染

ForEach

image.png

事件传递

子组件和父组件同时定义对应的函数,在父组件初始化子组件的时候,在对应的函数参数的闭包中调用父组件的函数。

@Component
struct Test {
  onClicked(str: string) {
    console.log(str)
  }

  build() {
    SubTest({ onClicked: (str: string) => {
      this.onClicked(str)
    } })
  }
}

@Component
struct SubTest {
  onClicked: (str: string) => void = () => {
  }

  build() {
    Text("Hello Word").onClick(() => {
      this.onClicked("Hello Word")
    })
  }
}

完整代码

// @ts-nocheck
@Entry
@Component
struct ZhihuComment {
  @State commentList: ReplyItem[] = [
    {
      id: 1,
      avatar: $r("app.media.avatar_1"),
      author: "路人甲",
      content: "你是我的小苹果,怎么爱你都不嫌多,啦啦啦啦啦~",
      time: "10-11",
      area: "天津",
      likeNum: 12,
    },
    {
      "id": 2,
      "avatar": $r("app.media.avatar_2"),
      "author": "随风而逝",
      "content": "窗外的风景如诗如画,心随风飘荡在无边的蓝天之下。",
      "time": "09-25",
      "area": "广州",
      "likeNum": 8
    },
    {
      "id": 3,
      "avatar": $r("app.media.avatar_3"),
      "author": "梦中人",
      "content": "在星光的照耀下,我仿佛能听见远方的微笑声。",
      "time": "08-14",
      "area": "成都",
      "likeNum": 15
    },
    {
      "id": 4,
      "avatar": $r("app.media.avatar_4"),
      "author": "雨巷行人",
      "content": "雨丝轻柔地落下,街角的小店散发着咖啡的香气。",
      "time": "07-02",
      "area": "上海",
      "likeNum": 10
    },
    {
      "id": 5,
      "avatar": $r("app.media.avatar_5"),
      "author": "星辰之梦",
      "content": "璀璨的星空映照着心灵深处的梦想,让人感叹生命的辽阔。",
      "time": "06-19",
      "area": "北京",
      "likeNum": 20
    },
    {
      "id": 6,
      "avatar": $r("app.media.avatar_6"),
      "author": "山水之恋",
      "content": "山谷间传来悠扬的笛声,溪水潺潺流过,宛如仙境般的美妙。",
      "time": "05-08",
      "area": "南京",
      "likeNum": 18
    },
    {
      "id": 7,
      "avatar": $r("app.media.avatar_7"),
      "author": "烟雨江南",
      "content": "江南的烟雨中,一缕忧伤被吹散,留下的是宁静和安详。",
      "time": "04-17",
      "area": "杭州",
      "likeNum": 13
    },
    {
      "id": 8,
      "avatar": $r("app.media.avatar_8"),
      "author": "岁月流转",
      "content": "时光荏苒,岁月如梭,每个瞬间都是生命的珍贵记忆。",
      "time": "03-29",
      "area": "重庆",
      "likeNum": 25
    },
    {
      "id": 9,
      "avatar": $r("app.media.avatar_9"),
      "author": "橙黄橘绿",
      "content": "阳光洒在稻田上,金黄的颜色让人感受到大自然的丰饶。",
      "time": "02-11",
      "area": "西安",
      "likeNum": 16
    },
    {
      "id": 10,
      "avatar": $r("app.media.avatar_10"),
      "author": "梧桐树下",
      "content": "悠扬的风铃声在梧桐树下回荡,时光仿佛静止。",
      "time": "01-05",
      "area": "深圳",
      "likeNum": 22
    },
    {
      "id": 11,
      "avatar": $r("app.media.avatar_11"),
      "author": "静谧夜空",
      "content": "夜幕降临,星光点点,宁静的夜空中弥漫着宝石般的梦幻。",
      "time": "12-23",
      "area": "武汉",
      "likeNum": 11
    },
    {
      "id": 12,
      "avatar": $r("app.media.avatar_12"),
      "author": "流年如梦",
      "content": "沿着小巷漫步,古老的石板路记录着岁月的故事。",
      "time": "11-08",
      "area": "南昌",
      "likeNum": 17
    },
    {
      "id": 13,
      "avatar": $r("app.media.avatar_13"),
      "author": "琴音轻扬",
      "content": "琴音轻扬,悠扬的旋律在空气中飘荡,引人陶醉。",
      "time": "10-30",
      "area": "福州",
      "likeNum": 14
    },
    {
      "id": 14,
      "avatar": $r("app.media.avatar_14"),
      "author": "画中人生",
      "content": "画笔在画布上舞动,勾勒出生命中丰富的色彩。",
      "time": "09-15",
      "area": "济南",
      "likeNum": 19
    },
    {
      "id": 15,
      "avatar": $r("app.media.avatar_15"),
      "author": "茶香袅袅",
      "content": "茶香袅袅,温暖的气息在宁静的午后悠然散开。",
      "time": "08-03",
      "area": "青岛",
      "likeNum": 9
    },
    {
      "id": 16,
      "avatar": $r("app.media.avatar_16"),
      "author": "晨曦微光",
      "content": "晨曦微光洒在大地上,新的一天开始了。",
      "time": "07-21",
      "area": "沈阳",
      "likeNum": 21
    },
    {
      "id": 17,
      "avatar": $r("app.media.avatar_17"),
      "author": "翩翩起舞",
      "content": "花瓣飘落,舞动的姿态如梦如幻,轻盈而美丽。",
      "time": "06-06",
      "area": "长沙",
      "likeNum": 23
    },
    {
      "id": 18,
      "avatar": $r("app.media.avatar_18"),
      "author": "海浪之梦",
      "content": "海浪拍打着沙滩,梦幻般的音乐在耳畔回荡。",
      "time": "05-27",
      "area": "厦门",
      "likeNum": 16
    },
    {
      "id": 19,
      "avatar": $r("app.media.avatar_19"),
      "author": "空山新雨",
      "content": "空山新雨后,清新的空气中弥漫着花香和泥土的味道。",
      "time": "04-02",
      "area": "南宁",
      "likeNum": 12
    },
    {
      "id": 20,
      "avatar": $r("app.media.avatar_20"),
      "author": "古城回忆",
      "content": "古城的记忆在石板路上延续,岁月的痕迹如诗如画。",
      "time": "03-14",
      "area": "哈尔滨",
      "likeNum": 14
    }
  ]
  headerReply: ReplyItem = {
    id: 10086,
    avatar: $r("app.media.header_icon"),
    author: "小苹果",
    content: "你是我的小苹果,怎么爱你都不嫌多,啦啦啦啦啦~",
    time: "10-11",
    area: "天津",
    likeNum: 120,
  }

  onClickLike(item: ReplyItem) {
    console.log(`点了 id=${item.id}`)
    if (item.id === 10086) {
      this.headerReply = { ...item }
    } else {
      const clickIndex = this.commentList.findIndex(i => i.id === item.id)
      this.commentList.splice(clickIndex, 1, item)
    }
  }

  onReply(item: ReplyItem) {
    this.commentList.unshift(item)
  }

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      Scroll() {
        Column({ space: 20 }) {
          BannerView()
          CommentView({ item: this.headerReply, onClickLike: (cellItem: ReplyItem) => {
            this.onClickLike(cellItem)
          } })
          Divider()
            .strokeWidth(6)
            .color("#f4f4f4")
          Row() {
            Text(`回复${this.commentList.length}`)
              .width("100%")
              .fontWeight(FontWeight.Bold)
              .margin({ left: 8 })
          }

          ForEach(this.commentList, (item: ReplyItem, index: number) => {
            CommentView({ item: item, onClickLike: (cellItem: ReplyItem) => {
              this.onClickLike(cellItem)
            } })
          })
        }
      }
      .padding({ bottom: 54 })

      CommentInputView({ onReply: (item: ReplyItem) => {
        this.onReply(item)
      } })
        .width("100%")
        .height(54)
        .backgroundColor(Color.White)
        .border({
          color: "#f4f5f6",
          width: { top: 1 }
        })
    }
  }
}

@Component
struct BannerView {
  build() {
    Row() {
      Image($r("app.media.back_icon"))
        .height(24)
        .aspectRatio(1)
        .backgroundColor("#F5F5F5")
        .borderRadius(12)
        .margin({ left: 8 })
      Text("评论回复")
        .layoutWeight(1)
        .textAlign(TextAlign.Center)
        .margin({ right: 39 })
    }
    .height(50)
    .border({
      color: "#f4f4f4",
      width: {
        bottom: 0.5
      }
    })
  }
}

@Component
struct CommentView {
  @State item: Partial<ReplyItem> = {}
  onClickLike: (item: ReplyItem) => void = () => {
  }

  build() {
    Row() {
      Image(this.item.avatar)
        .width(44)
        .aspectRatio(1)
        .borderRadius(22)
        .margin({ left: 8 })
      Column({ space: 10 }) {
        Text(this.item.author)
          .fontSize(20)
          .fontStyle(FontWeight.Bold)
        Text(this.item.content)

        Row() {
          Text(`${this.item.time} .IP属地${this.item.area}`)
            .fontColor("#c3c4c5")
          Row({ space: 8 }) {
            Image($r("app.media.hart_icon"))
              .width(20)
              .fillColor(this.item.likeFlag ? "red" : "#c3c4c5")
            Text(this.item.likeNum.toString())
              .fontColor("#c3c4c5")
          }.onClick(() => {
            if (this.item.likeFlag) {
              this.item.likeNum--
            } else {
              this.item.likeNum++
            }
            this.item.likeFlag = !this.item.likeFlag
            this.onClickLike(this.item as ReplyItem)
          })
        }
        .width("100%")
        .justifyContent(FlexAlign.SpaceBetween)
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)
      .margin({ left: 8, right: 8 })
    }
    .alignItems(HorizontalAlign.Start)
  }
}

@Component
struct CommentInputView {
  @State content: string = ""
  onReply: (item: ReplyItem) => void = () => {
  }

  build() {
    Row() {
      TextInput({ placeholder: "请输入回复~", text: this.content })
        .layoutWeight(1)
        .backgroundColor("#f4f5f6")
        .onChange((value) => {
          this.content = value
        })
      Button("回复")
        .margin({ left: 10, right: 10 })
        .height(44)
        .onClick(() => {
          if (this.content === "") {
            AlertDialog.show({ message: "不能回复为空" })
          } else {
            const item: ReplyItem = {
              id: Date.now(),
              avatar: $r("app.media.header_icon"),
              author: "哲大爷",
              content: this.content,
              time: `${new Date().getMonth() + 1}-${new Date().getDate()}`,
              area: "北京",
              likeNum: 0,
              likeFlag: false
            }
            this.onReply(item)
            this.content = ""
          }
        })
    }
    .height(50)
    .width("100%")
    .margin({ left: 8, right: 8 })
  }
}

export class ReplyItem {
  id: number = 0
  avatar: string | Resource = ""
  author: string = ""
  content: string = ""
  time: string = ""
  area: string = ""
  likeNum: number = 0
  likeFlag?: boolean = false
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容