鸿蒙 HarmonyOS NEXT星河版APP应用开发-ArkTS面向对象及组件化UI开发应用实例

一、ArkTS语言-Class类

Class类

  • 简介:类是用于创建对象模板,同时类声明也会引入一个新类型,可定义其示例属性、方法和构造函数。
  • 扩展:
// ?. 表示可选连操作符,

基本语法
class 类名{
  // 属性
  // 构造方法
  // 方法
}
// 实例化对象
const class1:类名= new 类名()

对象-属性
class   Person {
   name:string=""
   age :number=0
    like?:string
}
const user1 :Person = new Person();
console.log(JSON.stringify(user1))
// 可选链操作符,存在打印,不存在也不会报错。
console.log("like>>>",user1?.like)

image.png

对象-构造方法
class   Person {
   name:string
   age :number
    like?:string

  constructor(name:string,age:number,like?:string) {
    this.name = name
    this.age =age
    this.like= like
  }
}
const user1 :Person = new Person("张三",33);
console.log(JSON.stringify(user1))
const user2 :Person = new Person("李四",12,"运动");
console.log(JSON.stringify(user2))

image.png

对象-方法
class   Person {
   name:string
   age :number
    like?:string

  constructor(name:string,age:number,like?:string) {
    this.name = name
    this.age =age
    this.like= like
  }
  // 方法
  play(){
    console.log(`${this.name}喜欢${this.like}`)
  }
}
const user1 :Person = new Person("张三",33);
console.log(JSON.stringify(user1))
const user2 :Person = new Person("李四",12,"运动");
user2.play()
console.log(JSON.stringify(user2))

image.png

对象-静态属性和静态方法
// 类直接调用,不用new

class Tools{
  static version:number=1.1
  static show (){
    console.log("此类的作用是提供工具")
  }
}
console.log(Tools.version.toString())
Tools.show()

image.png

  • 对象-继承extends和super关键字
import { componentSnapshot } from '@kit.ArkUI';
class Animal{
  name:string
  voice :string

  constructor(name:string,voice:string) {
    this.name = name
    this.voice = voice
  }
  animalVOice(){
    console.log("动物声音:",this.voice)
  }
}
class Cat extends  Animal{
  color:string;
  constructor(name:string,voice:string,color:string) {
  super(name,voice)
    this.color=color
  }
}
class Dog extends  Animal{
}
const  cat1:Cat = new Cat("猫","喵","white")
console.log(`${cat1.name}的颜色是:${cat1.color}`)
cat1.animalVOice()
const  dog1:Dog = new Dog("狗","汪")
dog1.animalVOice()

image.png

对象-判断类型:instanceof
class Person{
}
class  Student extends  Person{
}
let stu1:Person = new Student()
console.log("result=>>>>>",stu1 instanceof Person)
console.log("result=>>>>>",stu1 instanceof Student)
let arr1:number[]=[]
console.log("result=>>>>>",arr1 instanceof  Array?arr1.length:null)

image.png

对象-访问修饰符
/*
readonly:只读,(只能读取不能修改) 例:Math.PI
private :私有的(只能在自己的类中使用)
protected :在自己和子类中可以访问
public (默认)
*/

剩余参数

说明:不定数量的参数

// 求和
function sum (num1:number,num2:number,...nums:number[]):number{
  let result = num1+num2
  for (let numsElement of nums) {
    result+=numsElement
  }
  return result
}
console.log("result>>>>",sum(1,2,3,4,5,3,56))
console.log("result>>>>",sum(1,2))

image.png

展开运算符

// 只能用在数组上
// 功能:合并数组

function sum (num1:number,num2:number,nums?:number[]):number{
  let result = num1+num2
  if(nums instanceof Array){
    for (let numsElement of nums) {
      result+=numsElement
    }
  }
  return result
}
let arr1:number[]=[123,33]
let arr2:number[]=[23,32,32]
console.log("result>>>>",sum(1,2,[...arr1,...arr2]))
console.log("result>>>>",sum(1,2))

image.png

接口继承

关键字:extends

interface  IAnimal{
  name:string,

}
interface ICat extends IAnimal{
  color:string
}
let cat1:ICat = {
  name:'cat',
  color:'red'
}
console.log(JSON.stringify(cat1))

image.png

接口实现

关键字:implements

interface  IAnimal{
  // 限制,实现该该接口,必须有该 接口中的属性和方法
  show():void
}
class  Animal  implements  IAnimal{
  name:string

  constructor(name:string) {
    this.name =name
  }
  show(){
    console.log("show")
  }
}
class  Cat extends Animal{
  show(){
    console.log("cat")
  }
}
let cat1 :Animal = new Cat("cat")
cat1.show()

image.png

泛型

泛型函数
/*
语法:
function 函数名 <Type>(temp:Type):Type{
  return temp
}
*/ 
function isType<Type>(temp:Type):void{
  console.log(typeof  temp)
}
isType("1")
isType(1)
isType(false)

image.png

泛型约束
// 给传递的类型参数,添加限制
interface  ISize{
  size():number;
}
function fn <T extends  ISize >(item :T){
  console.log("result>>",item.size())
}
class ISizeClass implements  ISize{
  size(): number {
    return 1
  }
}
let iSizeClass:ISize = new ISizeClass();
fn(iSizeClass)

image.png

多个泛型约束
interface  ISize{
  size():number;
}
function fn <T extends  ISize,T2 >(item :T,num:T2){
  console.log("result>>",item.size())
  console.log("result>>>",num)
}
class ISizeClass implements  ISize{
  size(): number {
    return 1
  }
}
let iSizeClass:ISize = new ISizeClass();
fn<ISize,number>(iSizeClass,1)

image.png

泛型接口
interface  IUser<T>{
    id:()=>T
}

let user1:IUser<number>= {
  id(){
    return 1;
  }
}
console.log("result>>>>",user1.id())

image.png

泛型类
class Person<T>{
  id:T

  constructor(id:T) {
    this.id =id
  }
  setId (id:T){
    this.id=id
  }
  getId():T{
    return this.id
  }
}
let user1:Person<number> = new Person<number>(1)
console.log("result>>>>>",user1.getId())

image.png

模块化

认识:一个ets文件就是一个模块,且每个模块之间都是独立的。

默认导入和导出

重点:只能导出一个值

/*
默认导出:指一个模块,只能默认导出的一个值或对象。使用时,可以自定义导入名称。
*/
 // 导出语法:
export default 需要导出的内容
// 默认导入语法:
import 自定义名称 from "模块路径"
// index2.ets
exprot default 1
// Index.ets
import num from "./index2"
console.log("result>>",num)

image.png
image.png

按需导入导出

重点:按照需要可导出导入多个值
注意:导入的变量名需要和导出的一致,如需要更改使用as关键字

  • 方式一:单个逐一导出
//导出 
export  let num1:number=1
export  let num2:number=2
// 导入
import {num1 as num11,num2} from "./index2" // 需要什么导入什么,
console.log("result>>>",num11)
console.log("result>>>",num2)

image.png

  • 方式一:统一导出
// 导出
let num1:number=1
let num2:number=2
export {num1,num2}
// 导出和方法一一致

全部导入

重点:使用*号

//  导出
let num1:number=1
let num2:number=2
export {num1,num2}

// 导入
import * as nums from "./index2"
console.log("result>>>",nums.num1)
console.log("result>>>",nums.num2)

image.png

二、自定义组件

基本使用
// 自定义
@Component
struct Header {
  build() {
    Row(){
    Text("首页")
    }.width("100%").height(50).backgroundColor("#fff59a9a").justifyContent(FlexAlign.Center)
  }
}

@Entry
@Component
struct Index {
  build() {
    Column(){
      Header()
    }.width("100%").height("100%")
  }
}

image.png

组件拆分

image.png

// header.ets
@Component
export   struct Header {
  build() {
    Row(){
      Text("首页")
    }.width("100%").height(50).backgroundColor("#fff59a9a").justifyContent(FlexAlign.Center)
  }
}

// Index.ets
import {Header} from "../component/Header"
@Entry
@Component
struct Index {
  build() {
    Column(){
      Header()
    }.width("100%").height("100%")
  }
}

image.png

成员变量和成员函数
import {Header} from "../component/Header"
@Component
struct navItem {
  // 成员变量
  title:string="标题"
  message:string="消息"
  // 成员方法
  show=()=>{
    AlertDialog.show({message:this.message})
  }
  // 普通方法 ,内部用,不能外部修改
  show1 (){}
  build() {
    Column(){
      Row(){
        Text(this.title)
        Text("更多》").fontColor("#ff5d5b5b").onClick(()=>{
          this.show()
        })


      }.border({width:{bottom:1}}).width("100%").height(40).padding({left:5,right:5}).justifyContent(FlexAlign.SpaceBetween)
      Column(){
       Text("内容")
      }.padding(5)

    }
      .width("100%").height(150).backgroundColor("#fff8c6c6").borderRadius(10)
  }
}
@Entry
@Component
struct Index {
  build() {
    Column(){
      Header()
      navItem({title:"新闻",message:'新闻'}).width("100%").padding(10)
      navItem({title:"娱乐",message:"娱乐"}).width("100%").padding(10)
    }.width("100%").height("100%")
  }
}

image.png

BuilderParam 传递UI
import {Header} from "../component/Header"
@Component
struct navItem {
  // 成员变量
  title:string="标题"
  message:string="消息"
  // 成员方法
  show=()=>{
    AlertDialog.show({message:this.message})
  }
  // 普通方法 ,内部用,不能外部修改
  show1 (){}
  // 定义BuilderParam 接收外部传入的ui,并设置默认值
  @BuilderParam  ContentBuilder :()=>void = this.defaultBuilder
  @Builder
  defaultBuilder(){
    // 默认UI
    Text("默认")
  }
  build() {
    Column(){
      Row(){
        Text(this.title)
        Text("更多》").fontColor("#ff5d5b5b").onClick(()=>{
          this.show()
        })
      }.border({width:{bottom:1}}).width("100%").height(40).padding({left:5,right:5}).justifyContent(FlexAlign.SpaceBetween)
      Column(){
       this.ContentBuilder()
      }.padding(5)

    }
      .width("95%").height(150).backgroundColor("#fff8c6c6").borderRadius(10).margin(10)
  }
}
@Entry
@Component
struct Index {
  build() {
    Column(){
      Header()
      navItem({title:"新闻",message:'新闻'}){
             // 外部UI
        Text("新闻1")
      }
      navItem({title:"娱乐",message:"娱乐"}){
        // 外部UI
        Button("娱乐1")
      }
    }.width("100%").height("100%")
  }
}

image.png

多个BuilderParam 传递UI
/*
多个和单个的区别就是:多个需要使用参数的方式来传递参数
*/

@Component
struct CardItem {

  @BuilderParam TitleBuild :()=>void = this.titleDefaultBuild
  @BuilderParam ContentBuild :()=>void = this.ContentDefaultBuild

  @Builder
  titleDefaultBuild(){
  Text("a")
}
  @Builder
  ContentDefaultBuild(){
    Text("b")
  }

  build() {
    Column(){
      Row(){
        this.TitleBuild()
      }.width("100%").height(40).border({width:{bottom:1}}).padding(5)
      Column(){
        this.ContentBuild()

      }.layoutWeight(1).width("100%").alignItems(HorizontalAlign.Start).padding(10)
    }.width("100%").height(150).borderRadius(10).backgroundColor("#fff8cccc")
  }
}
@Entry
@Component
struct Index {
  @Builder
  TitleBuild(){
    Text("新闻")
  }
  @Builder
  ContentBuild(){
    Text("新闻内容")
  }
  build() {
    Column(){
      CardItem({
        TitleBuild:this.TitleBuild,
        ContentBuild:this.ContentBuild
      }).margin({bottom:10})
      CardItem()

    }.width("100%").height("100%").padding(10)
  }
}

image.png

三、状态管理

概述

当运行时的状态变量变化时,带来UI的重新渲染,在ArkUI中统称为状态管理机制。
状态变量特征:被装饰器修饰。
image.png

@State
  • @State修饰简单类型的数据,数据修改时会实时的刷新页面。
  • 但是修饰复杂类型的数据时,虽然数据也能够被修改,但是页面只会刷新全部刷新一次后,后续只刷新复杂类型的第一层数据。
  • 解决办法就是将内容的变量整个重新赋值。
  • 使用Object.key(Object)可以知道复杂数据类型的第一层数据的变量值
/*

*/ 
interface  Cat{
  name:string
  age:number
}

interface  Person{
  name:string
  cat:Cat
}

@Entry
@Component
struct Index {
  @State message:string ="hello"
   @State user:Person={
     name:"小明",
     cat:{
       name:'cat1',
       age:1
     }
   }

  build() {
    Column(){
      Text(Object.keys(this.user).toString())
      Text(this.message)
      Text(JSON.stringify(this.user))
      Text(this.user.cat.name)
      Text(this.user.cat.age.toString())
      Button("修改").onClick(()=>{
        this.message="小小"
        this.user.name="a"
        this.user.cat.name="cat2"
        this.user.cat.age++
        console.log(JSON.stringify(this.user))

      })


    }.width("100%").height("100%").padding(10)
  }
}

image.png

@Prop
  • prop可以接收父组件传递的数据。但是不能修改父组件的数据
  • 如果子组件想要修改父组件的数据,需要父组件定义一个成员方法并传递给子组件尽心修改(其作用还是父组件自己修改自己的值,只是将修改的操作给了子组件。)
@Component
struct SItem {
   @Prop SMessage:string=''
  change=()=>{}
  build() {

  Column(){
    Text(`子组件》》》${this.SMessage}`).width(100).height(100).backgroundColor("#fff")
    Button("a1").onClick(()=>{
      this.change()
    })

  }
  }
}
@Entry
@Component
struct Index {
  @State FMessage:string="f"
  build() {

    Column(){
      Text(`父组件>>${this.FMessage}`)
      Button("F").onClick(()=>{
        this.FMessage="ff"
      })

    SItem({SMessage:this.FMessage,change:()=>{
      this.FMessage="fff"
    }})
      
    }.width(200).height(200).backgroundColor("#fff3b1b1")
  }
}

image.png

案例-父子传值
@Component
struct numItem {
  @Prop num:number=0
  sub=()=>{}
  add=()=>{}
  build() {
    Row({space:10}){
      Button("-").onClick(()=>{
        this.sub()
      })
      Text(this.num.toString())
      Button("+").onClick(()=>{
        this.add()
      })

    }.width("100%").height(60)
  }
}
@Entry
@Component
struct Index {
  @State num:number=0
  build() {
    Column(){
      numItem({
        num:this.num,
        sub:()=>{
        this.num--
      },
        add:()=>{this.num++}
      })
    }.width("100%").height("100%").backgroundColor("#ffefefef")
  }
}

image.png

@Link双向同步
// 子组件和父组件可以同时修改数据并同步


@Component
struct Sitem {
  @Link s:number
  build() {
    Column(){
      Text(this.s.toString())
      Button("a").onClick(()=>{
        this.s++
      })
    }
  }
}


@Entry
@Component
struct Index3 {
  @State message:number=0
  build() {
    Column(){
      Text(this.message.toString())
      Button("a").onClick(()=>{
          this.message++
      })
      Sitem({s:this.message})
    }.width("100%").height("100%")

  }
}

@Provide和@Consume后代组件
// 不需要传参
// 父组件使用@Component
// 子组件使用:@Provide 
// 使用条件:父组件和子组件绑定的变量名需要一致。

@Component
struct Sitem {
  @Consume message:number
  build() {
    Column(){
      Text(this.message.toString())
      Button("a").onClick(()=>{
        this.message++
      })
    }
  }
}
@Entry
@Component
struct Index3 {
  @Provide message:number=0
  build() {
    Column(){
      Text(this.message.toString())
      Button("a").onClick(()=>{
          this.message++
      })
      Sitem()
    }.width("100%").height("100%")

  }
}

@ Observed&@ ObjectLink嵌套数组属性变化

针对:因为装饰器仅能观察到第一层的变化,对于数据对象操作麻烦。
作用:用于在涉及嵌套对象或数组的场景中进行双向数据同步
注意:ObjectLink修饰符不能使用在Entry修饰的组件中。
image.png

// 页面可以刷新多层对象的嵌套,
// 注意:只是针对@ObjectLink的,没有加ObjectLink的的不会刷新

interface  IPerson{
  id:number
  name:string
  age:number

}

@Observed
class Person{
  id:number
  name:string
  age:number

  constructor(obj:IPerson) {
    this.id =obj.id
    this.name = obj.name
    this.age = obj.age

  }
}
@Component
struct Pitem {
  @ObjectLink info :Person
  build() {
    Row(){
      Text(JSON.stringify(this.info))
      Button("+").onClick(()=>{
        this.info.age++
      })
    }

  }
}
@Entry
@Component
struct Index3 {
  @State persons:Person[] = [
    new Person({
      id:1,
      name:"varin",
      age:1
    })
  ]
  build() {
    Column(){
      Pitem({info:this.persons[0]})
    }.width("100%").height("100%")

  }
}

四、路由管理

创建多个页面

  • 方法一

image.png

  • 方法二:
/*
第一步:创建一个新的ets文件
第二步:找到main_pages.json文件
路径:src/main/resources/base/profile/main_pages.json
第三步:配置
*/
{
  "src":[
    "pages/Index",
    "pages/Index2" // 新加的
  ]
}

image.png

页面跳转和后退

// 可以返回用pushUrl
// 不可以返回用replaceUrl()
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Login {
  build() {
    Column(){
      Text("login")
      Button("跳转到首页,可以回退").onClick(()=>{
        router.pushUrl({
          url:"pages/Index"
        })

      })
      Button("跳转到首页,不能回退").onClick(()=>{
        router.replaceUrl({
          url:"pages/Index"
        })

      }).margin({top:10})
    }.width("100%").height("100%")
  }
}

页面栈

说明:页面栈是用来存储程序运行时页面的一种数据结构,遵循先进后出的原则。
页面栈的最大容量为32个页面。

// 获取页面栈长度
router.getLength()
// 清空页面栈
router.clear()

路由模式

Standard(默认,一直添加页面栈)
Single:如果页面已存在,会将最近同URL页面移到栈顶

路由传参

// 传递
router.pushUrl({

  url:"地址,
    params:{
  // 数据
    }
})
// 获取
 const amessage = router.getParams() as Obj;
    console.log(JSON.stringify(router.getParams()))

案例:

// Login.ets

import { router } from '@kit.ArkUI'

@Entry
@Component
struct Login {
  build() {
    Column(){
      Text("login")
      Button("跳转到首页,可以回退").onClick(()=>{
        router.pushUrl({
          url:"pages/Index",
          params:{
            name:'abc'
          }
        })

      })
      Button("跳转到首页,不能回退").onClick(()=>{
        router.replaceUrl({
          url:"pages/Index"
        })

      }).margin({top:10})
    }.width("100%").height("100%")
  }
}

image.png

// Index.ets
import { router } from '@kit.ArkUI'

interface  Obj{
  name:string
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  aboutToAppear(): void {
    // 拿到的默认是一个对象,需要取值要设置参数的类型
    const amessage = router.getParams() as Obj;
    console.log(JSON.stringify(router.getParams()))
    this.message = amessage.name
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

image.png

五、生命周期

image.png

六、Stage模型

简介

应用模型是系统为开发者提供的应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制
简而言之:应用模型就是应用的施工图纸,他规范化了:程序运行流程,项目结构,文件功能等。

Stage模型-目录

image.png

app.Json5应用配置

{
  "app": {
    "bundleName": "cn.varin.myapplication", // 包名
    "vendor": "example", // 描述
    "versionCode": 1000000, // 版本号
    "versionName": "1.0.0", // 给用户看的版本号
    "icon": "$media:app_icon",// 应用图标
    "label": "$string:app_name" //应用名
  }
}

UIAbility组件

image.png

七、常用组件及案例

扩展-List组件

  • 简介:列表是一种容器,当列表项达到一定数量时,超过List容器组件大小时,会自动滚动
  • 语法:
  • image.png

扩展-将图标当做字体引入

步骤一:https://www.iconfont.cn/寻找图标添加到项目中,完成后下载,解压后将解压后的文件添加
到app项目中。

image.png

// 步骤二:注册
// 导入模块:
import font from '@ohos.font'
// 在生命周期函数类注册字体
  aboutToAppear(): void {
    font.registerFont({
      familyName:"fontIcons", // 名称任意
      familySrc:'/fontIcons/iconfont.ttf' // 步骤一存放的位置
    })
  }

// 步骤三:使用
// 文本框中填写的值,参考icon网站打包中的html文件
// fontFamily 填写自定义字体名称
  Text("\ue651").fontFamily("fontIcons").fontSize(20).fontColor("#ffea2222")

image.png
效果:
image.png

掘金评论案例

结构

image.png

实体类
// 准备评论的数据类
export class CommentData {
  avatar: string; // 头像
  name: string; // 昵称
  level: number; //用户等级
  likeNum: number; //点赞数量
  commentTxt: string; //评论内容
  isLike: boolean; //是否喜欢
  levelIcon: Resource // level等级
  timeString: string // 发布时间-基于时间戳处理后,展示给用户看的属性(2天前)
  time: number // 时间戳-数字格式的日期

  constructor(avatar: string, name: string, time: number, level: number, lickNum: number, commentTxt: string, isLike: boolean, ) {
    this.avatar = avatar
    this.name = name
    this.timeString = this.convertTime(time) // 拿到的时间格式, 通常是时间戳的格式  1645820201123
    this.time = time
    this.level = level
    this.likeNum = lickNum
    this.commentTxt = commentTxt
    this.isLike = isLike
    this.levelIcon = this.convertLevel(this.level) // 基于等级数字, 转换成图片路径
  }

  convertTime(timestamp: number) {
    // 获取当前的时间戳
    const currentTimestamp = new Date().getTime();
    const timeDifference = (currentTimestamp - timestamp) / 1000; // 转换为秒

    if (timeDifference < 5 || timeDifference == 0) {
      return "刚刚";
    } else if (timeDifference < 60) {
      return `${Math.floor(timeDifference)}秒前`;
    } else if (timeDifference < 3600) {
      return `${Math.floor(timeDifference / 60)}分钟前`;
    } else if (timeDifference < 86400) {
      return `${Math.floor(timeDifference / 3600)}小时前`;
    } else if (timeDifference < 604800) {
      return `${Math.floor(timeDifference / 86400)}天前`;
    } else if (timeDifference < 2592000) {
      return `${Math.floor(timeDifference / 604800)}周前`;
    } else if (timeDifference < 31536000) {
      return `${Math.floor(timeDifference / 2592000)}个月前`;
    } else {
      return `${Math.floor(timeDifference / 31536000)}年前`;
    }
  }

  // 基于传入的level,转换成图片路径
  convertLevel(level: number) {
    const iconList = [
      $r('app.media.level_1'),
      $r('app.media.level_2'),
      $r('app.media.level_3'),
      $r('app.media.level_4'),
      $r('app.media.level_5'),
      $r('app.media.level_6'),
    ]
    return iconList[level]
  }
}

// 封装一个方法, 创建假数据
export const createListRange = (): CommentData[] => {
  let result: CommentData[] = new Array()
  result = [
    new CommentData(`https://fastly.picsum.photos/id/770/600/600.jpg?hmac=tuK9EHg1ifTU3xKAiZj2nHSdWy4mk7enhylgOc2BW7E`, "雪山飞狐", 1645820201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '23年一年干完的事😂😂😂真的非常仓促', false),
    new CommentData(`https://fastly.picsum.photos/id/225/600/600.jpg?hmac=v97zt_t4mxeyMttX_m09pxhCvftiTxFR1MMBZi5HQxs`, "千纸鹤", 1677356201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), 'Netty对象池源码分析来啦!juejin.cn欢迎点赞[奸笑]', false),
    new CommentData(`https://fastly.picsum.photos/id/122/600/600.jpg?hmac=1oA93YbjYVt96DcJcGQ5PLthzjUsdtrnBQaM0USBozI`, "烟雨江南", 1688772201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '有一个不听劝的Stable Diffusion出图的小伙伴,找我给她装填脑。 一个资深的IT工程师不能受这个委屈。', false),
    new CommentData(`https://fastly.picsum.photos/id/654/600/600.jpg?hmac=ewnK6Bx_MKQLJa9waZOV1xNO7--K5oSwCShtz1JDYw8`, "魔法小精灵", 1697484201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '有一点可以确信 前后端开发界限越来越模糊。后端可以不写 但是不能不会。', false),
    new CommentData(`https://fastly.picsum.photos/id/345/600/600.jpg?hmac=EQflzbIadAglm0RzotyKXM2itPfC49fR3QE7eW_UaPo`, "独行侠", 1704067200000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '今天看到一个帖子,挺有意思的。', false),
    new CommentData(`https://fastly.picsum.photos/id/905/600/600.jpg?hmac=DvIKicBZ45DEZoZFwdZ62VbmaCwkK4Sv7rwYzUvwweU`, "枫叶飘零", 1706745600000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '我想了搞钱的路子, 1:投资理财, 后来发下,不投资就是最好的理财, 2:买彩票,后来发现彩票都是人家预定好的,根本不是随机的,卒, 3:开店创业,隔行如隔山,开店失败,卒。', false),
    new CommentData(`https://fastly.picsum.photos/id/255/600/600.jpg?hmac=-lfdnAl71_eAIy1OPAupFFPh7EOJPmQRJFg-y7lRB3s`, "星空漫步", 1707523200000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '优胜劣汰,自然选择吧,没什么好怪的。都是crud,招个大学生就能干了。', false),
    new CommentData(`https://fastly.picsum.photos/id/22/600/600.jpg?hmac=QEZq7KUHwBZCt3kGSEHMwJlZfnzCxCeBgHjYj7iQ-UY`, "剑指苍穹", 1708300800000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '白嫖ChatGPT4的功能。然后,抱着试一试的态度把玩了一下。发现真的好用。', false),
    new CommentData(`https://fastly.picsum.photos/id/439/600/600.jpg?hmac=LC9k_bzrN0NhKRyV62fou3ix3cRFZKNfAyXgxGs6zh8`, "黑暗王国", 1708646400000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '字数越少,事情越大。', false),
  ]
  return result
}



首页
import InfoTop from "../components/InfoTop"
import InfoItem from "../components/InfoItem"
import font from '@ohos.font'
import InfoBtm from '../components/InfoBtn'
import {CommentData,createListRange} from  "../model/CommentData"
import data from '@ohos.telephony.data'
@Entry
@Component
struct Index {
  aboutToAppear(): void {
    font.registerFont({
      familyName:"fontIcons",
      familySrc:'/fontIcons/iconfont.ttf'
    })
  }
  // 列表数据
  @State commentList:CommentData[] = createListRange()

  handleLike(index:number){
    // AlertDialog.show({
    //   message:index.toString()
    // })
    let dataItem = this.commentList[index]
    if (dataItem.isLike) {

      dataItem.likeNum -=1
    }else{
      dataItem.likeNum +=1
    }
    dataItem.isLike = !dataItem.isLike
    this.commentList.splice(index,1,dataItem)
  }

  // 添加评论
  onAddComment(txt:string){
  const item:CommentData=
    new CommentData(`https://fastly.picsum.photos/id/439/600/600.jpg?hmac=LC9k_bzrN0NhKRyV62fou3ix3cRFZKNfAyXgxGs6zh8`, "Varin", new Date().getTime(), Math.floor(Math.random()*6) , Math.floor(Math.random()*100), txt, false)
   this.commentList= [item , ...this.commentList]
  }

  // 排序
  handleSort(type:number){
    if(type==0){
      this.commentList.sort((a,b)=>{
        return b.time-a.time
      })
    }else if(type==1){
      this.commentList.sort((a,b)=>{
        return b.likeNum-a.likeNum
      })
    }

  }
  @State num:number=0
  build() {
    Column(){
      // 头
      InfoTop({
        onSort:(type:number)=>{
          this.handleSort(type)
        }

      })

      // 内容
        List(){
          ForEach(this.commentList,(item:CommentData,index)=>{
            ListItem(){
              InfoItem({
                itemObj:item,
                index:index,
              onHandleLike:(index:number)=>{
                // 在外层包一层箭头函数,让this指向父
                this.handleLike(index)
              }
              })
            }
          })
        }.layoutWeight(1)
      .padding({bottom:10})
      .divider({
        color: "#ffd9d9d9",
        startMargin:10,
        endMargin:10,
        strokeWidth:1
      })
      // 底
     InfoBtm({

       addComment:(txt:string)=>{
         this.onAddComment(txt)
       }

     })
    }.width("100%").height("100%").backgroundColor("#ffefefef")
  }
}

头部区组件
@Extend(Button)
function InfoTopBtn(isp:boolean){
  .width(46)
    .height(32)
    .fontSize(12)
    .padding({left:5,right:5})
    .backgroundColor(isp?"#fff":"#f7f8fa")
    .fontColor(isp?"#2f2e33":"#8e9298")
    .border({width:1,color:isp?'#e4e5e6':"#f7f8fa"})
}
@Component
struct InfoTop {
 @State isP:boolean = true
  onSort=(type:number)=>{}
  build() {
    Row(){
    Text("全部评论").fontSize(18).fontWeight(700)
      Row(){
        Button("最新",{stateEffect:false}).InfoTopBtn(this.isP).onClick(()=>{
          this.isP=true
          this.onSort(0)
        })

        Button("最热",{stateEffect:false}).InfoTopBtn(!this.isP).onClick(()=>{
          this.isP=false
          this.onSort(1)
        })
      }

    }.width("100%").height(60).backgroundColor("#f7f8fa").padding(15).justifyContent(FlexAlign.SpaceBetween)

  }
}
export  default InfoTop

内容区组价
import { defaultAppManager } from '@kit.AbilityKit'
import { CommentData } from '../model/CommentData'

@Component
struct InfoItem {
  @Prop itemObj:CommentData
  @Prop index:number
  onHandleLike=(index:number)=>{}
  build() {
    Column(){
      Row() {
        Image(this.itemObj.avatar).width(30).aspectRatio(1).borderRadius(15)
        Text(this.itemObj.name).fontSize(13).fontColor(Color.Gray).margin({left:8})
        Image(this.itemObj.levelIcon).width(20).aspectRatio(1).margin({left:8})

      }.margin({top:10})
      Text(this.itemObj.commentTxt).fontSize(13)
        .fontColor(Color.Black).margin({left:40,bottom:8})
      Row() {

        Text(this.itemObj.timeString).fontSize(13).fontColor(Color.Gray).margin({left:40})
        Row(){
          Image(this.itemObj.isLike?$r("app.media.like_select"):$r("app.media.like_unselect")).width(15)
          Text(this.itemObj.likeNum.toString()).fontSize(13).margin({left:8}).fontColor(this.itemObj.isLike?Color.Blue:Color.Gray)
        }.onClick(()=>{
  // AlertDialog.show({message:})
          this.onHandleLike(this.index)

        })


      }.width("100%").justifyContent(FlexAlign.SpaceBetween)

    }.padding({left:15,right:15,top:5,bottom:5}).alignItems(HorizontalAlign.Start)
  }
}
export  default  InfoItem

底部组件
@Component
struct InfoBtm {
  @State txt:string = ''
  addComment=(txt:string)=>{}
  build() {
    Row(){
      Row(){
        Text("\ue621").fontSize(18).fontFamily("fontIcons").margin({left:10})
          TextInput({placeholder:"写评论...",text:$$this.txt}).fontSize(18).backgroundColor(Color.Transparent)
            .onSubmit(()=>{
              // AlertDialog.show({message:this.txt})
              this.addComment(this.txt)
            })

      }.height(40).backgroundColor("#f5f6f5").borderRadius(20)
      .margin({left:15,right:20,top:10,bottom:10}).layoutWeight(1)
      Text("\ue651").fontSize(20).fontFamily("fontIcons").margin({left:6,right:6})
      Text("\ue6a7").fontSize(20).fontFamily("fontIcons").margin({left:6,right:6})


    }.height(60).backgroundColor("#ffffffff").width("100%")

  }
}
export  default InfoBtm

效果图:
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VarYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值