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

本文涉及的产品
多模态交互后付费免费试用,全链路、全Agent
简介: 本文介绍了ArkTS语言中的Class类、泛型、接口、模块化、自定义组件及状态管理等核心概念,并结合代码示例讲解了对象属性、构造方法、继承、静态成员、访问修饰符等内容,同时涵盖了路由管理、生命周期和Stage模型等应用开发关键知识点。

一、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)

(三) 对象-构造方法
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))

(四) 对象-方法
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))

(五) 对象-静态属性和静态方法
// 类直接调用,不用new
class Tools{
  static version:number=1.1
  static show (){
    console.log("此类的作用是提供工具")
  }
}
console.log(Tools.version.toString())
Tools.show()

  • 对象-继承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()

(六) 对象-判断类型: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)

(七) 对象-访问修饰符
/*
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))

三、展开运算符

// 只能用在数组上
// 功能:合并数组
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))

四、接口继承

关键字:extends

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

五、接口实现

关键字: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()

六、泛型

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

(二) 泛型约束
// 给传递的类型参数,添加限制
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)

(三) 多个泛型约束
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)

(四) 泛型接口
interface  IUser<T>{
    id:()=>T
}
let user1:IUser<number>= {
  id(){
    return 1;
  }
}
console.log("result>>>>",user1.id())

(五) 泛型类
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())

七、模块化

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

(一) 默认导入和导出

重点:只能导出一个值

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

(二) 按需导入导出

重点:按照需要可导出导入多个值

注意:导入的变量名需要和导出的一致,如需要更改使用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)

  • 方式一:统一导出
// 导出
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)

二、自定义组件

(一) 基本使用
// 自定义
@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%")
  }
}

(二) 组件拆分

// 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%")
  }
}

(三) 成员变量和成员函数
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%")
  }
}

(四) 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%")
  }
}

(五) 多个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)
  }
}

三、状态管理

(一) 概述

当运行时的状态变量变化时,带来UI的重新渲染,在ArkUI中统称为状态管理机制。

状态变量特征:被装饰器修饰。

(二) @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)
  }
}

(三) @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")
  }
}

(四) 案例-父子传值
@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")
  }
}

(五) @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修饰的组件中。

// 页面可以刷新多层对象的嵌套,
// 注意:只是针对@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%")
  }
}

四、路由管理

一、创建多个页面

  • 方法一

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

二、页面跳转和后退

// 可以返回用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%")
  }
}

// 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%')
  }
}

五、生命周期

六、Stage模型

1. 简介

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

2. Stage模型-目录

3. app.Json5应用配置

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

4. UIAbility组件

七、常用组件及案例

一、扩展-List组件

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

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

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

// 步骤二:注册
// 导入模块:
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")

效果:

三、掘金评论案例

(一) 结构

(二) 实体类
// 准备评论的数据类
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

效果图:

目录
相关文章
|
9天前
|
人工智能 运维 安全
|
7天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
8天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
676 23
|
8天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
14天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
1106 110
|
人工智能 数据可视化 数据挖掘
Quick BI 体验&征文有奖!
瓴羊生态推出Quick BI 征文激励计划,鼓励用户分享数据分析实践经验与技术洞察,征集高质量原创文章。内容围绕AI功能体验与BI案例实践,设季奖、年奖及参与奖,优秀作者可获现金奖励、产品内测资格及官方认证形象。投稿截止至2026年3月31日。
Quick BI 体验&征文有奖!