一、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
效果图: