准备
课程介绍
工具安装
了解arkts
ts基本语法
快速入门
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.fontColor('#36D') // RGB颜色
.onClick(()=>{
this.message='Hello ArkTS!'
})
}
.width('100%')
}
.height('100%')
}
}
ArkUI组件
image
// entry/src/main/module.json5
{
"module": {
"requestPermissions": [
{
'name': 'ohos.permission.INTERNET'
}
],
// ...
}
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.fontColor('#36D') // RGB颜色
.onClick(() => {
this.message = 'Hello ArkTS!'
})
// 网络图片
// Image('https://c-ssl.duitang.com/uploads/blog/202310/06/q4Sy272asxzxN2g.jpeg')
// resources下的图片
Image($r('app.media.icon'))
.width(250) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
}
.width('100%')
}
.height('100%')
}
}
text
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Image($r('app.media.icon'))
.width(250) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
// 动态文本(国际化)
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
// entry/src/main/resources/en_US/element/string.json
// entry/src/main/resources/base/element/string.json
{
"string": [
// ...
{
"name": "width_label",
"value": "Image Width: "
}
]
}
// entry/src/main/resources/zh_CN/element/string.json
{
"string": [
// ...
{
"name": "width_label",
"value": "图片宽度: "
}
]
}
text input
@Entry
@Component
struct Index {
@State imageWidth: number = 30
build() {
Row() {
Column() {
Image($r('app.media.icon'))
.width(this.imageWidth) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
// 动态文本(国际化)
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
// placeholder: 提示词 text: 默认值
// toFixed -> 数字转字符串, 参数为保留几位小数
TextInput({ placeholder: '请输入图片宽度', text: this.imageWidth.toFixed(0) })
.width(250)
.backgroundColor('#36D')
.type(InputType.Number)
.onChange(value => {
this.imageWidth = parseInt(value)
})
}
.width('100%')
}
.height('100%')
}
}
@Entry
@Component
struct Index {
@State imageWidth: number = 30
build() {
Row() {
Column() {
Image($r('app.media.icon'))
.width(this.imageWidth) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
// 动态文本(国际化)
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
// placeholder: 提示词 text: 默认值
// toFixed -> 数字转字符串, 参数为保留几位小数
TextInput({ placeholder: '请输入图片宽度', text: this.imageWidth.toFixed(0) })
.width(250)
.backgroundColor('#36D')
.type(InputType.Number)
.onChange(value => {
this.imageWidth = parseInt(value)
})
Button('缩小')
.width(80)
.fontSize(20)
.onClick(() => {
if (this.imageWidth >= 10) {
this.imageWidth -= 10
}
})
Button('放大')
.width(80)
.fontSize(20)
.type(ButtonType.Normal)
.onClick(() => {
if (this.imageWidth < 300) {
this.imageWidth += 10
}
})
}
.width('100%')
}
.height('100%')
}
}
slider
@Entry
@Component
struct Index {
@State imageWidth: number = 30
build() {
Row() {
Column() {
Image($r('app.media.icon'))
.width(this.imageWidth) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
// 动态文本(国际化)
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
// placeholder: 提示词 text: 默认值
// toFixed -> 数字转字符串, 参数为保留几位小数
TextInput({ placeholder: '请输入图片宽度', text: this.imageWidth.toFixed(0) })
.width(250)
.backgroundColor('#36D')
.type(InputType.Number)
.onChange(value => {
this.imageWidth = parseInt(value)
})
Button('缩小')
.width(80)
.fontSize(20)
.onClick(() => {
if (this.imageWidth >= 10) {
this.imageWidth -= 10
}
})
Button('放大')
.width(80)
.fontSize(20)
.type(ButtonType.Normal)
.onClick(() => {
if (this.imageWidth < 300) {
this.imageWidth += 10
}
})
Slider({
min: 100,
max: 300,
value: this.imageWidth,
step: 10
})
.width('90%')
.blockColor('#36D')
.trackThickness(7)
.showTips(true)
.onChange(value => {
this.imageWidth = value
})
}
.width('100%')
}
.height('100%')
}
}
页面布局 column row
@Entry
@Component
struct Index {
@State imageWidth: number = 30
build() {
// Column({ space: 30 }) {
Column() {
Row() {
Image($r('app.media.icon'))
.width(this.imageWidth) // 可以用数字(默认单位vp)或百分比(字符串)
.interpolation(ImageInterpolation.High) // 高分辨率
}
.width('100%')
.height(400)
.justifyContent(FlexAlign.Center)
Row() {
// 动态文本(国际化)
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
// placeholder: 提示词 text: 默认值
// toFixed -> 数字转字符串, 参数为保留几位小数
TextInput({ placeholder: '请输入图片宽度', text: this.imageWidth.toFixed(0) })
.width(150)
.backgroundColor('#FFF')
.type(InputType.Number)
.onChange(value => {
this.imageWidth = parseInt(value)
})
}
.width('100%')
.padding({ left: 14, right: 14 })
.justifyContent(FlexAlign.SpaceBetween)
Divider()
.width('91%')
Row() {
Button('缩小')
.width(80)
.fontSize(20)
.onClick(() => {
if (this.imageWidth >= 10) {
this.imageWidth -= 10
}
})
Button('放大')
.width(80)
.fontSize(20)
.type(ButtonType.Normal)
.onClick(() => {
if (this.imageWidth < 300) {
this.imageWidth += 10
}
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.margin({ top: 35, bottom: 35 })
Slider({
min: 100,
max: 300,
value: this.imageWidth,
step: 10
})
.width('90%')
.blockColor('#36D')
.trackThickness(5)
.showTips(true)
.onChange(value => {
this.imageWidth = value
})
}
.width('100%')
}
}
渲染控制
class Item {
name: string
image: ResourceStr
price: number
constructor(name: string, image: ResourceStr, price: number) {
this.name = name
this.image = image
this.price = price
}
}
@Entry
@Component
struct Index {
// 商品数据
private items: Array- = [
new Item('图片1', $r('app.media.OIPC'), 1999),
new Item('图片2', $r('app.media.OIPC1'), 2999),
new Item('图片3', $r('app.media.OIPC2'), 3999),
new Item('图片4', $r('app.media.OIPC3'), 4999),
new Item('图片5', $r('app.media.OIPC4'), 5999),
new Item('图片6', $r('app.media.OIPC5'), 6999),
]
build() {
Column({ space: 8 }) {
Row() {
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({ bottom: 20 })
ForEach(
this.items,
(item: Item) => {
Row({ space: 10 }) {
Image(item.image)
.width(100)
Column({ space: 4 }) {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('$ ' + item.price)
.fontSize(18)
.fontColor('F36')
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#fff')
.borderRadius(20)
.height(120)
.padding(10)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#efefef')
.padding(20)
}
}
class Item {
name: string
image: ResourceStr
price: number
discount: number
constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}
@Entry
@Component
struct Index {
// 商品数据
private items: Array- = [
new Item('图片1', $r('app.media.OIPC'), 1999, 500),
new Item('图片2', $r('app.media.OIPC1'), 2999),
new Item('图片3', $r('app.media.OIPC2'), 3999),
new Item('图片4', $r('app.media.OIPC3'), 4999),
new Item('图片5', $r('app.media.OIPC4'), 5999),
new Item('图片6', $r('app.media.OIPC5'), 6999),
]
build() {
Column({ space: 8 }) {
Row() {
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.margin({ bottom: 20 })
ForEach(
this.items,
(item: Item) => {
Row({ space: 10 }) {
Image(item.image)
.width(100)
Column({ space: 4 }) {
if (item.discount) {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价: $ ' + item.price)
.fontSize(14)
.fontColor('#CCC')
.decoration({ type: TextDecorationType.LineThrough })
Text('折扣价: $ ' + (item.price - item.discount))
.fontSize(18)
.fontColor('#F36')
Text('补贴: $ ' + item.discount)
.fontSize(18)
.fontColor('#F36')
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('$ ' + item.price)
.fontSize(18)
.fontColor('F36')
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#fff')
.borderRadius(20)
.height(120)
.padding(10)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#efefef')
.padding(20)
}
}
List
class Item {
name: string
image: ResourceStr
price: number
discount: number
constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}
@Entry
@Component
struct Index {
// 商品数据
private items: Array- = [
new Item('图片1', $r('app.media.OIPC'), 1999, 500),
new Item('图片2', $r('app.media.OIPC1'), 2999),
new Item('图片3', $r('app.media.OIPC2'), 3999),
new Item('图片4', $r('app.media.OIPC3'), 4999),
new Item('图片5', $r('app.media.OIPC4'), 5999),
new Item('图片6', $r('app.media.OIPC5'), 6999),
]
build() {
Column({ space: 8 }) {
Row() {
Text('商品列表')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(30)
.margin({ bottom: 20 })
List({ space: 8 }) {
ForEach(
this.items,
(item: Item) => {
ListItem() {
Row({ space: 10 }) {
Image(item.image)
.width(100)
Column({ space: 4 }) {
if (item.discount) {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价: $ ' + item.price)
.fontSize(14)
.fontColor('#CCC')
.decoration({ type: TextDecorationType.LineThrough })
Text('折扣价: $ ' + (item.price - item.discount))
.fontSize(18)
.fontColor('#F36')
Text('补贴: $ ' + item.discount)
.fontSize(18)
.fontColor('#F36')
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('$ ' + item.price)
.fontSize(18)
.fontColor('F36')
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#fff')
.borderRadius(20)
.height(120)
.padding(10)
}
}
)
}
.width('100%')
.layoutWeight(1) // 布局权重, 此时上面那行占30, 剩下的高都是该list的
}
.width('100%')
.height('100%')
.backgroundColor('#efefef')
.padding(20)
}
}
自定义组件
class Item {
name: string
image: ResourceStr
price: number
discount: number
constructor(name: string, image: ResourceStr, price: number, discount: number = 0) {
this.name = name
this.image = image
this.price = price
this.discount = discount
}
}
import { Header } from '../components/CommonComponents'
// 全局自定义构建函数
// @Builder function ItemCard(item: Item) {
// Row({ space: 10 }) {
// Image(item.image)
// .width(100)
// Column({ space: 4 }) {
// if (item.discount) {
// Text(item.name)
// .fontSize(20)
// .fontWeight(FontWeight.Bold)
// Text('原价: $ ' + item.price)
// .fontSize(14)
// .fontColor('#CCC')
// .decoration({ type: TextDecorationType.LineThrough })
// Text('折扣价: $ ' + (item.price - item.discount))
// .fontSize(18)
// .fontColor('#F36')
// Text('补贴: $ ' + item.discount)
// .fontSize(18)
// .fontColor('#F36')
// } else {
// Text(item.name)
// .fontSize(20)
// .fontWeight(FontWeight.Bold)
// Text('$ ' + item.price)
// .fontSize(18)
// .fontColor('F36')
// }
// }
// .height('100%')
// .alignItems(HorizontalAlign.Start)
// }
// .width('100%')
// .backgroundColor('#fff')
// .borderRadius(20)
// .height(120)
// .padding(10)
// }
// 全局公共样式
// 如果样式需要某种类型的组件才有, 需要用extend继承
// extend不能写在组件内
@Extend(Text) function priceText() {
.fontColor('#36D')
.fontSize(18)
}
@Entry
@Component
struct Index {
// 商品数据
private items: Array- = [
new Item('图片1', $r('app.media.OIPC'), 1999, 500),
new Item('图片2', $r('app.media.OIPC1'), 2999),
new Item('图片3', $r('app.media.OIPC2'), 3999),
new Item('图片4', $r('app.media.OIPC3'), 4999),
new Item('图片5', $r('app.media.OIPC4'), 5999),
new Item('图片6', $r('app.media.OIPC5'), 6999),
]
// 局部自定义构建函数
@Builder ItemCard(item: Item) {
Row({ space: 10 }) {
Image(item.image)
.width(100)
Column({ space: 4 }) {
if (item.discount) {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('原价: $ ' + item.price)
.fontSize(14)
.fontColor('#CCC')
.decoration({ type: TextDecorationType.LineThrough })
Text('折扣价: $ ' + (item.price - item.discount))
.priceText()
Text('补贴: $ ' + item.discount)
.priceText()
} else {
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('$ ' + item.price)
.priceText()
}
}
.height('100%')
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.backgroundColor('#fff')
.borderRadius(20)
.height(120)
.padding(10)
}
// 局部公共样式
@Styles fillScreen() {
.width('100%')
.height('100%')
.backgroundColor('#EFEFEF')
.padding(20)
}
build() {
Column({ space: 8 }) {
Header({ title: '商品列表' })
.margin({ bottom: 20 })
List({ space: 8 }) {
ForEach(
this.items,
(item: Item) => {
ListItem() {
// ItemCard(item) // 全局
this.ItemCard(item) // 局部
}
}
)
}
.width('100%')
.layoutWeight(1) // 布局权重, 此时上面那行占30, 剩下的高都是该list的
}
// .width('100%')
// .height('100%')
// .backgroundColor('#efefef')
// .padding(20)
.fillScreen()
}
}
// entry/src/main/ets/components/CommonComponents.ets
@Component
export struct Header {
private title: ResourceStr
build() {
// 标题
Row() {
Image($r('app.media.back_64'))
.width(30)
Text(this.title)
.fontSize(30)
.fontWeight(FontWeight.Bold)
// 这三个元素固定占一定大小, 要让他们分开排列, 不是用justifyContent
// 而是用blank占位
Blank() // 占据剩余空间
Image($r('app.media.refresh'))
.width(30)
}
.width('100%')
.height(30)
// .margin({ bottom: 20 })
}
}
状态管理
@State
class Person {
name: string
age: number
gf: Person
constructor(name: string, age: number, gf?: Person) {
this.name = name
this.age = age
this.gf = gf
}
}
@Entry
@Component
struct Index3 {
idx: number = 1
@State p: Person = new Person('Jack', 12)
@State gfs: Person[] = [
new Person('gf1', 18),
new Person('gf2', 17),
]
build() {
Column() {
Text(`${this.p.name} : ${this.p.age}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.p.age++
})
Button('添加')
.onClick(() => {
this.gfs.push(new Person('女友' + this.idx++, 20))
})
Text('=女友列表=')
.fontSize(50)
.fontWeight(FontWeight.Bold)
ForEach(
this.gfs,
(p, idx) => {
Row() {
Text(`${p.name} : ${p.age}`)
.fontSize(30)
.onClick(() => {
// p.age++
// 修改数组(插入/替换/删除...)才能更新视图
this.gfs[idx] = new Person(p.name, p.age + 1)
})
Button('删除')
.onClick(() => {
// 从idx位置开始, 删除一个元素
this.gfs.splice(idx, 1)
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
}
)
}
.width('100%')
.height('100%')
.padding(20)
}
}
@Component
struct Index2 {
@State p: Person = new Person('Jack', 12, new Person('Rose', 18))
build() {
Column() {
Text(`${this.p.name} : ${this.p.age}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.p.age++
})
Text(`${this.p.gf.name} : ${this.p.gf.age}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.p.gf.age++
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
@Component
struct Index1 {
// error: Variables decorated by '@State', '@StorageLink', '@StorageProp',
// and '@Provide' must be initialized locally.
// @State message: string
@State name: string = 'Jack'
@State age: number = 12
build() {
Column() {
Text(`${this.name} : ${this.age}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.age++
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
任务统计案例
// 任务类
class Task {
static id: number = 1
// 任务名称
name: string = `任务${Task.id++}`
// 任务状态
finished: boolean = false
}
// 统一的卡片样式
@Styles function card() {
.width('95%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
}
// 任务完成的样式
@Extend(Text) function finishedTask() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor('#B1B2B1')
}
@Entry
@Component
struct PropPage {
// 总任务数量
@State totalTask: number = 0
// 已完成任务数量
@State finishTask: number = 0
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 更新总任务数
this.totalTask = this.tasks.length
// 更新完成任务的数量
this.finishTask = this.tasks.filter(item => item.finished).length
}
build() {
Column({ space: 10 }) {
// 1. 任务进度卡片
Row() {
Text('任务进度: ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
// 堆叠容器
Stack() {
Progress({
value: this.finishTask,
total: this.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.totalTask.toString())
.fontSize(24)
}
}
}
.card()
.margin({ top: 20, bottom: 10 })
.justifyContent(FlexAlign.SpaceEvenly)
// 2. 新增任务按钮
Button('新增任务')
.width(200)
.onClick(() => {
// 新增任务
this.tasks.push(new Task())
// 更新总任务数
// this.totalTask = this.tasks.length
this.handleTaskChange()
})
// 3. 任务列表
List({ space: 10 }) {
ForEach(
this.tasks,
(item: Task, idx) => {
ListItem() {
Row() {
Text(item.name)
.fontSize(20)
Checkbox()
.select(item.finished)
.onChange(val => {
// 更新任务状态
item.finished = val
// 更新完成任务的数量
// this.finishTask = this.tasks.filter(item => item.finished).length
this.handleTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
}
.swipeAction({ end: this.DeleteButton(idx) })
}
)
}
.width('100%')
// 要有高度才能拖动
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
@Builder DeleteButton(index: number) {
Button() {
Image($r('app.media.delete'))
.fillColor(Color.White)
.width(20)
}
.width(40)
.height(40)
.type(ButtonType.Circle)
.backgroundColor(Color.Red)
.margin(5)
.onClick(() => {
// 删除一个
this.tasks.splice(index, 1)
// 更新数据
this.handleTaskChange()
})
}
}
@Prop @Link @Provide @Consume
// 任务类
class Task {
static id: number = 1
// 任务名称
name: string = `任务${Task.id++}`
// 任务状态
finished: boolean = false
}
// 统一的卡片样式
@Styles function card() {
.width('95%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
}
// 任务完成的样式
@Extend(Text) function finishedTask() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor('#B1B2B1')
}
// 任务统计信息 (测试object类型)
class StatInfo {
// 总任务数量
totalTask: number = 0
// 已完成任务数量
finishTask: number = 0
}
@Entry
@Component
struct PropPage {
// 总任务数量
// @State totalTask: number = 0
// 已完成任务数量
// @State finishTask: number = 0
// 统计信息
@State stat: StatInfo = new StatInfo()
build() {
Column({ space: 10 }) {
// 1. 任务进度卡片
TaskStatistics({
// 父组件的数据需要传给子组件进行数据同步
// finishTask: this.finishTask,
// totalTask: this.totalTask
finishTask: this.stat.finishTask,
totalTask: this.stat.totalTask
})
// 2. 任务列表
// TaskList({
// // 父组件的数据需要传给子组件进行数据同步
// finishTask: $finishTask,
// // A '@Link' decorated attribute must be initialized with '$'.
// // 双向绑定的数据, 传递的是引用
// // totalTask: this.totalTask
// totalTask: $totalTask
// })
TaskList({
stat: $stat
})
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskStatistics {
// 总任务数量
@Prop totalTask: number
// 已完成任务数量
@Prop finishTask: number
build() {
// 1. 任务进度卡片
Row() {
Text('任务进度: ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
// 堆叠容器
Stack() {
Progress({
value: this.finishTask,
total: this.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.totalTask.toString())
.fontSize(24)
}
}
}
.card()
.margin({ top: 20, bottom: 10 })
.justifyContent(FlexAlign.SpaceEvenly)
}
}
@Component
struct TaskList {
// 总任务数量
// @Link totalTask: number
// 已完成任务数量
// @Link finishTask: number
@Link stat: StatInfo
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 更新总任务数
// this.totalTask = this.tasks.length
// 更新完成任务的数量
// this.finishTask = this.tasks.filter(item => item.finished).length
// 更新总任务数
this.stat.totalTask = this.tasks.length
// 更新完成任务的数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
build() {
Column() {
// 2. 新增任务按钮
Button('新增任务')
.width(200)
.onClick(() => {
// 新增任务
this.tasks.push(new Task())
// 更新总任务数
// this.totalTask = this.tasks.length
this.handleTaskChange()
})
// 3. 任务列表
List({ space: 10 }) {
ForEach(
this.tasks,
(item: Task, idx) => {
ListItem() {
Row() {
Text(item.name)
.fontSize(20)
Checkbox()
.select(item.finished)
.onChange(val => {
// 更新任务状态
item.finished = val
// 更新完成任务的数量
// this.finishTask = this.tasks.filter(item => item.finished).length
this.handleTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
}
.swipeAction({ end: this.DeleteButton(idx) })
}
)
}
.width('100%')
// 要有高度才能拖动
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
}
}
@Builder DeleteButton(index: number) {
Button() {
Image($r('app.media.delete'))
.fillColor(Color.White)
.width(20)
}
.width(40)
.height(40)
.type(ButtonType.Circle)
.backgroundColor(Color.Red)
.margin(5)
.onClick(() => {
// 删除一个
this.tasks.splice(index, 1)
// 更新数据
this.handleTaskChange()
})
}
}
// 任务统计信息 (测试object类型)
class StatInfo {
// 总任务数量
totalTask: number = 0
// 已完成任务数量
finishTask: number = 0
}
@Entry
@Component
struct PropPage {
// 总任务数量
// @State totalTask: number = 0
// 已完成任务数量
// @State finishTask: number = 0
// 统计信息
// @State stat: StatInfo = new StatInfo()
@Provide stat: StatInfo = new StatInfo()
build() {
Column({ space: 10 }) {
// 1. 任务进度卡片
TaskStatistics({
// 父组件的数据需要传给子组件进行数据同步
// finishTask: this.finishTask,
// totalTask: this.totalTask
// @Provider和@Consume会自动传, 不需要显示传递
// finishTask: this.stat.finishTask,
// totalTask: this.stat.totalTask
})
// 2. 任务列表
// TaskList({
// // 父组件的数据需要传给子组件进行数据同步
// finishTask: $finishTask,
// // A '@Link' decorated attribute must be initialized with '$'.
// // 双向绑定的数据, 传递的是引用
// // totalTask: this.totalTask
// totalTask: $totalTask
// })
TaskList({
// stat: $stat
// @Provider和@Consume会自动传, 不需要显示传递
})
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskStatistics {
// 总任务数量
// @Prop totalTask: number
// 已完成任务数量
// @Prop finishTask: number
@Consume stat: StatInfo
build() {
// 1. 任务进度卡片
Row() {
Text('任务进度: ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
// 堆叠容器
Stack() {
Progress({
// value: this.finishTask,
// total: this.totalTask,
value: this.stat.finishTask,
total: this.stat.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.stat.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.stat.totalTask.toString())
.fontSize(24)
}
}
}
.card()
.margin({ top: 20, bottom: 10 })
.justifyContent(FlexAlign.SpaceEvenly)
}
}
@Component
struct TaskList {
// 总任务数量
// @Link totalTask: number
// 已完成任务数量
// @Link finishTask: number
// @Link stat: StatInfo
@Consume stat: StatInfo
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 更新总任务数
// this.totalTask = this.tasks.length
// 更新完成任务的数量
// this.finishTask = this.tasks.filter(item => item.finished).length
// 更新总任务数
this.stat.totalTask = this.tasks.length
// 更新完成任务的数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
build() {
///
}
///
}
@Observed @ObjectLink
// 任务类
@Observed
class Task {
static id: number = 1
// 任务名称
name: string = `任务${Task.id++}`
// 任务状态
finished: boolean = false
}
// 统一的卡片样式
@Styles function card() {
.width('95%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(15)
.shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
}
// 任务完成的样式
@Extend(Text) function finishedTask() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor('#B1B2B1')
}
// 任务统计信息 (测试object类型)
class StatInfo {
// 总任务数量
totalTask: number = 0
// 已完成任务数量
finishTask: number = 0
}
@Entry
@Component
struct PropPage {
// 总任务数量
// @State totalTask: number = 0
// 已完成任务数量
// @State finishTask: number = 0
// 统计信息
// @State stat: StatInfo = new StatInfo()
@Provide stat: StatInfo = new StatInfo()
build() {
Column({ space: 10 }) {
// 1. 任务进度卡片
TaskStatistics({
// 父组件的数据需要传给子组件进行数据同步
// finishTask: this.finishTask,
// totalTask: this.totalTask
// @Provider和@Consume会自动传, 不需要显示传递
// finishTask: this.stat.finishTask,
// totalTask: this.stat.totalTask
})
// 2. 任务列表
// TaskList({
// // 父组件的数据需要传给子组件进行数据同步
// finishTask: $finishTask,
// // A '@Link' decorated attribute must be initialized with '$'.
// // 双向绑定的数据, 传递的是引用
// // totalTask: this.totalTask
// totalTask: $totalTask
// })
TaskList({
// stat: $stat
// @Provider和@Consume会自动传, 不需要显示传递
})
}
.width('100%')
.height('100%')
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskStatistics {
// 总任务数量
// @Prop totalTask: number
// 已完成任务数量
// @Prop finishTask: number
@Consume stat: StatInfo
build() {
// 1. 任务进度卡片
Row() {
Text('任务进度: ')
.fontSize(30)
.fontWeight(FontWeight.Bold)
// 堆叠容器
Stack() {
Progress({
// value: this.finishTask,
// total: this.totalTask,
value: this.stat.finishTask,
total: this.stat.totalTask,
type: ProgressType.Ring
})
.width(100)
Row() {
Text(this.stat.finishTask.toString())
.fontSize(24)
.fontColor('#36D')
Text(' / ' + this.stat.totalTask.toString())
.fontSize(24)
}
}
}
.card()
.margin({ top: 20, bottom: 10 })
.justifyContent(FlexAlign.SpaceEvenly)
}
}
@Component
struct TaskList {
// 总任务数量
// @Link totalTask: number
// 已完成任务数量
// @Link finishTask: number
// @Link stat: StatInfo
@Consume stat: StatInfo
// 任务数组
@State tasks: Task[] = []
handleTaskChange() {
// 更新总任务数
// this.totalTask = this.tasks.length
// 更新完成任务的数量
// this.finishTask = this.tasks.filter(item => item.finished).length
// 更新总任务数
this.stat.totalTask = this.tasks.length
// 更新完成任务的数量
this.stat.finishTask = this.tasks.filter(item => item.finished).length
}
build() {
Column() {
// 2. 新增任务按钮
Button('新增任务')
.width(200)
.onClick(() => {
// 新增任务
this.tasks.push(new Task())
// 更新总任务数
// this.totalTask = this.tasks.length
this.handleTaskChange()
})
// 3. 任务列表
List({ space: 10 }) {
ForEach(
this.tasks,
(item: Task, idx) => {
ListItem() {
// 方法传给子组件, 方法里用到的this会改变, 可能出错
// TaskItem({ item, onTaskChange: this.handleTaskChange })
TaskItem({ item, onTaskChange: this.handleTaskChange.bind(this) }) // 绑定this
}
.swipeAction({ end: this.DeleteButton(idx) })
}
)
}
.width('100%')
// 要有高度才能拖动
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
}
}
@Builder DeleteButton(index: number) {
Button() {
Image($r('app.media.delete'))
.fillColor(Color.White)
.width(20)
}
.width(40)
.height(40)
.type(ButtonType.Circle)
.backgroundColor(Color.Red)
.margin(5)
.onClick(() => {
// 删除一个
this.tasks.splice(index, 1)
// 更新数据
this.handleTaskChange()
})
}
}
@Component
struct TaskItem {
@ObjectLink item: Task
onTaskChange: () => void
build() {
Row() {
if (this.item.finished) {
Text(this.item.name)
.finishedTask()
} else {
Text(this.item.name)
.fontSize(20)
}
Checkbox()
.select(this.item.finished)
.onChange(val => {
// 更新任务状态
this.item.finished = val
// 更新完成任务的数量
// this.handleTaskChange()
this.onTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
}
}
页面路由
import router from '@ohos.router'
class RouterInfo {
// 页面路径
url: string
// 页面标题
title: string
constructor(url: string, title: string) {
this.url = url
this.title = title
}
}
@Entry
@Component
struct Index {
@State msg: string = '页面列表'
private routers: RouterInfo[] = [
new RouterInfo('pages/1_components/button', '按钮案例'),
new RouterInfo('pages/1_components/imagePage', '图片查看案例'),
new RouterInfo('pages/1_components/slider', '滑动条案例'),
new RouterInfo('pages/1_components/list', '列表案例'),
new RouterInfo('pages/1_components/DIYcomponent', '自定义组件案例'),
]
build() {
Column() {
Text(this.msg)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.height(80)
.onClick(() => {
this.msg = 'hello arkts'
})
List({ space: 15 }) {
ForEach(
this.routers,
(r, i) => {
ListItem() {
this.RouterItem(r, i + 1)
}
}
)
}
.layoutWeight(1)
.alignListItem(ListItemAlign.Center)
.width('100%')
}
}
@Builder
RouterItem(r: RouterInfo, i: number) {
Row() {
Text(i + '.')
.fontSize(20)
.fontColor(Color.White)
Blank()
Text(r.title)
.fontSize(20)
.fontColor(Color.White)
}
.width('90%')
.padding(12)
.backgroundColor('#38f')
.borderRadius(20)
.shadow({ radius: 6, color: '#4F000000', offsetX: 2, offsetY: 4 })
.onClick(() => {
// 跳转
router.pushUrl(
{
url: r.url,
params: {
id: i
}
},
router.RouterMode.Single,
err => {
if (err) {
console.log(`路由失败: errCode: ${err.code} errMsg: ${err.message}`)
}
}
)
})
}
}
动画
属性动画和显示动画