目录
八. ArkTS-语句-类型进阶与渲染控制
1. 对象进阶
1.1. 定义对象数组
1.2. 使用对象数组
2. 渲染控制 - ForEach
2.1. ForEach语法
2.2. ForEach使用优化代码
2.3. 案例-学生档案
实现思路
3. Math对象
4. 综合案例 -- 抽奖卡案例
4.1. 初始页面布局(静态)
4.2. 根据数据渲染初始页面
4.2.1. 准备响应数据
4.2.2. 渲染初始页面
4.3. 遮罩层静态布局
4.4. 遮罩层动态交互
4.4.1. 按钮交互
4.4.2. 图片数据
4.5. 中奖结果
4.5.1. 抽奖静态页面
4.5.2. 条件渲染-抽卡与抽奖页面
4.5.3. 抽奖数据处理
4.6. 再次抽奖
八. ArkTS-语句-类型进阶与渲染控制
今日核心
- 对象
- 渲染控制ForEach
1. 对象进阶
1.1. 定义对象数组
// 1. 定义接口
interface Person {
stuId: number
name: string
gender: string
age: number
}
// 2. 根据接口定义对象数组
let students: Person[] = [
{stuId: 1, name: '小红', gender: '女', age: 18},
{stuId: 2, name: '小明', gender: '男', age: 19},
{stuId: 3, name: '大强', gender: '男', age: 18}
]
1.2. 使用对象数组
- 访问某一个对象:
-
- 每一对象在数组中都是有对应的下标的, 可以通过 数组名[下标] 访问
- 访问某一对象的某一个属性
-
- 先找到要访问的对象, 在该对象的属性访问属性 数组名[下标].属性名 访问
- 依次访问每一个对象
-
- for ... of 进行访问即可
// 3. 访问属性 数组名[下标].属性名
console.log('小红的年龄是', students[0].age)
// 4. 遍历对象数组
for (const item of students) {
console.log('学生姓名是', item.name)
}
2. 渲染控制 - ForEach
当我们页面中的区域中有多个样式相同的小区域,只有内容数据不一样的时候, 为了提升代码的复用率,不需要一个一个的编写UI组件,我们可以将所有的数据整合成一个数组, 并采取ForEach进行循环渲染.如下图区域,如果我们分别实现每个栏目,代码太过于冗余,这时就可以使用ForEach进行渲染了
2.1. ForEach语法
ForEach基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。
- 语法: ForEach(arr, (item, index) => {})
参数 | 参数类型 | 是否必须 | 参数说明 |
arr | Array | 是 | 数据源, 根据该数组生成对应的UI组件渲染到页面中:
|
UI组件生成函数 | (item: any, index?: number) => void | 是 | UI组件生成函数
|
2.2. ForEach使用优化代码
@Entry
@Component
struct Index {
build() {
Column(){
Text('电子产品')
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
Text('精品服饰')
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
Text('母婴产品')
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
Text('影音娱乐')
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
Text('海外旅游')
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
}
.width('100%')
.height('100%')
}
}
完成代码
@Entry
@Component
struct Index {
@State
titles:string[] = ['电子产品', '精品服饰', '母婴产品', '影音娱乐', '海外旅游']
build() {
Column() {
ForEach(this.titles, (item: string, index: number) => {
Text(item)
.fontSize('24')
.fontWeight(700)
.fontColor(Color.Orange)
.width('100%')
.padding(15)
})
}
}
}
2.3. 案例-学生档案
模版代码
@Entry
@Component
struct Index {
build() {
Column() {
Row() {
Text(`姓名:张三,年龄:18`)
.fontSize(18)
.fontColor('#fff')
Button('年龄+1')
.backgroundColor('#e64133')
}
.width('100%')
.height(60)
.backgroundColor(Color.Orange)
.borderRadius(5)
.padding({left: 10, right: 10})
.margin({bottom: 10})
.justifyContent(FlexAlign.SpaceBetween)
}
.padding(20)
}
}
实现思路
- 准备数据
-
- 定义数组元素接口
- 根据接口创建对象数组保存数据
- ForEach循环渲染
-
- 使用数组元素的属性(item.属性名)动态填充UI组件对应的位置
interface Person {
stuId: number
name: string
gender: string
age: number
}
@Entry
@Component
struct Index {
@State
students: Person[] = [
{stuId: 1, name: '小红', gender: '女', age: 18},
{stuId: 2, name: '小明', gender: '男', age: 19},
{stuId: 3, name: '大强', gender: '男', age: 18}
]
build() {
Column() {
ForEach(this.students, (item: Person, index: number) => {
Row() {
Text(`姓名:${item.name},年龄:${item.age}`)
.fontSize(18)
.fontColor('#fff')
Button('年龄+1')
.backgroundColor('#e64133')
.onClick(() => {
// item.age += 1
this.students[index] = {
stuId: item.stuId,
name: item.name,
gender: item.gender,
age: item.age += 1
}
})
}
.width('100%')
.height(60)
.backgroundColor(Color.Orange)
.borderRadius(5)
.padding({left: 10, right: 10})
.margin({bottom: 10})
.justifyContent(FlexAlign.SpaceBetween)
})
}
.padding(20)
}
}
3. Math对象
Math 是一个内置对象,它拥有一些数学常数属性和数学函数方法, Math 用于 Number 类型数据的处理.
Math.random() | 否 |
|
Math.ceil() | 是 |
|
Math.floor() | 是 |
|
- 代码测试
// 1. 随机数
console.log('Math对象', Math.random()) // 0-1 之间的随机小数
// 2. 向上取整
console.log('Math对象', Math.ceil(1.1)) // 2
console.log('Math对象', Math.ceil(1.9)) // 2
// 3. 向下取整
console.log('Math对象', Math.floor(1.1)) // 1
console.log('Math对象', Math.floor(1.9)) // 1
- 求0--10之间的随机整数
// 0-10 之间的随机数
console.log('Math对象', Math.random() * 11)
// 0-10 之间的随机 整数
console.log('Math对象', Math.floor(Math.random() * 11))
4. 综合案例 -- 抽奖卡案例
实现如下图所示的效果
4.1. 初始页面布局(静态)
备注:无数据
@Entry
@Component
struct Index {
build() {
Column() {
// 一. 初始界面
Column() {
// 图片区域
Flex({wrap: FlexWrap.Wrap}) {
Badge({
count: 1,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
Badge({
count: 1,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
Badge({
count: 1,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
Badge({
count: 1,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
Badge({
count: 1,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
Badge({
count: 0,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r('app.media.bg_00'))
.width(80)
}
.margin(15)
}
.margin({top: 100, bottom: 50})
.padding({left: 15})
// 抽卡按钮
Button('立即抽卡')
.width(200)
.backgroundColor('#ED5B8C')
.stateStyles({
pressed: {
.backgroundColor('#EA446C')
}
})
.animation({
duration: 1000
})
}
}
}
}
4.2. 根据数据渲染初始页面
4.2.1. 准备响应数据
思路: 观察效果可以发现列表区域的每一项, 至少需要两个数据:
- 图片的地址
- 抽中的数量
// 初始界面卡片接口
interface Card {
url: string
num: number
}
// 初始界面卡片状态
@State
images: Card[] = [
{url: 'app.media.bg_00', num: 0},
{url: 'app.media.bg_01', num: 0},
{url: 'app.media.bg_02', num: 0},
{url: 'app.media.bg_03', num: 0},
{url: 'app.media.bg_04', num: 0},
{url: 'app.media.bg_05', num: 0}
]
4.2.2. 渲染初始页面
ForEach() 渲染 Badge组件
// 初始界面卡片接口
interface Card {
url: string
num: number
}
@Entry
@Component
struct Index {
// 初始界面卡片状态
@State
images: Card[] = [
{url: 'app.media.bg_00', num: 0},
{url: 'app.media.bg_01', num: 0},
{url: 'app.media.bg_02', num: 0},
{url: 'app.media.bg_03', num: 0},
{url: 'app.media.bg_04', num: 0},
{url: 'app.media.bg_05', num: 0}
]
build() {
Column() {
// 一. 初始界面
Column() {
// 图片区域
Flex({wrap: FlexWrap.Wrap}) {
ForEach(this.images, (item: Card, index: number) => {
Badge({
count: item.num,
style: {badgeSize: 16, badgeColor: '#f00', fontSize: 14}
}) {
Image($r(`${item.url}`))
.width(80)
}
.margin(15)
})
}
.margin({top: 100, bottom: 50})
.padding({left: 15})
// 抽卡按钮
Button('立即抽卡')
.width(200)
.backgroundColor('#ED5B8C')
.stateStyles({
pressed: {
.backgroundColor('#EA446C')
}
})
.animation({
duration: 1000
})
}
}
}
}
4.3. 遮罩层静态布局
单击“立即抽卡”按钮后显示的遮罩层
- 标签解构: Column > Text + Image + Button
样式枚举:
- 遮罩层整体样式:
宽度 -> 100% 高度 -> 100% 背景色 -> 'rgba(0,0,0,0.8)' 定位 内容主轴对齐方式 -> 居中
- 文字: 颜色 -> '#F5EBCF' 大小 -> 25 粗细 -> 600
- 图片: 外边距 -> 30 宽度 -> 200
- 按钮样式:
宽度 -> 200 高度 -> 50 背景色 -> 透明 边框 -> 粗细 2 颜色 '#FFF9E0'
Column({space: 30}) {
Text('获取生肖卡')
.fontColor('#F5EBCF')
.fontSize(25)
.fontWeight(600)
Image($r('app.media.img_00'))
.width(200)
Button('开心收下')
.width(200)
.height(50)
.backgroundColor(Color.Transparent)
.border({width: 2, color: '#FFF9E0'})
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.8)')
.justifyContent(FlexAlign.Center)
.position({x: 0, y: 0})
4.4. 遮罩层动态交互
4.4.1. 按钮交互
遮罩层的显示隐藏, 图片添加缩放效果
思路分析:
默认状态
- 界面隐藏
visibility(Visibility:Hidden)
- 生肖卡隐藏(缩放比例为0)
抽卡后状态
- 界面显示
visibility(Visibility:Visible)
- 生肖卡显示(缩放比例为1)
单击“开心收下”按钮
- 界面隐藏
// 是否显示生肖卡界面状态
@State
isVisible: boolean = false
// 生肖卡缩放状态
@State
imgX: number = 0
@State
imgY: number = 0
// 抽卡按钮
Button('立即抽卡')
.width(200)
.backgroundColor('#ED5B8C')
.stateStyles({
pressed: {
.backgroundColor('#EA446C')
}
})
.animation({
duration: 1000
})
.onClick(() => {
this.isVisible = true
this.imgX = 1
this.imgY = 1
})
// 二. 生肖卡界面
Column({space: 30}) {
Image($r('app.media.img_00'))
.width(200)
.scale({x: this.imgX, y: this.imgY})
.animation({duration: 400})
Button('开心收下')
.onClick(() => {
// 生肖卡界面隐藏;卡片隐藏
this.isVisible = false
this.imgX = 0
this.imgY = 0
})
}
// 使用状态控制整个界面显隐
.visibility(this.isVisible ? Visibility.Visible : Visibility.Hidden)
4.4.2. 图片数据
随机抽中一张生肖卡,单击“开心收下”按钮,显示对应的生肖卡,角标+1
思路分析:
- 随机显示生肖卡
- 卡片数量 +1
// 随机生肖卡序号
@State
randomIndex: number = -1
Button('立即抽卡')
.onClick(() => {
// 随机生肖卡 0-5
this.randomIndex = Math.floor(Math.random() * 6)
})
Column({space: 30}) {
// 使用生成的随机数筛选生肖卡
Image($r(`app.media.img_0${this.randomIndex}`))
Button('开心收下')
.onClick(() => {
// 收下卡片更新对象数组 -- 替换初始界面图片url和角标数字
this.images[this.randomIndex] = {
url: `app.media.img_0${this.randomIndex}`,
num: this.images[this.randomIndex].num += 1
}
})
}
4.5. 中奖结果
6张卡片数量均>=1,则可以抽奖一次(显示抽奖页面:奖品随机)
4.5.1. 抽奖静态页面
// 抽奖页面-遮罩层
Column({space: 50}) {
Text('恭喜获得手机一部')
.fontColor('#F5EBCF')
.fontSize(25)
.fontWeight(600)
Image($r('app.media.hw'))
.width(300)
Button('再来一次')
.width(200)
.height(50)
.backgroundColor(Color.Transparent)
.border({width: 2, color: '#FFF9E0'})
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.8)')
.justifyContent(FlexAlign.Center)
.position({x: 0, y: 0})
4.5.2. 条件渲染-抽卡与抽奖页面
集齐6种生肖卡显示抽奖页面,否则显示抽卡页面
// 是否中奖状态
@State
isGet: boolean = false
// 三. 抽奖中奖界面
if (this.isGet) {
Column({space: 50}) {}
}
4.5.3. 抽奖数据处理
判断是否可以抽奖:定义count变量保存已经获得的卡片种类数量(如果每个生肖卡数量>=1,则count+1)
如果count == 6,说明已经得到了6种卡,则可以抽奖,渲染抽中的奖品图片
思路分析:
声明一个变量 保存 中奖的结果 、声明一个变量保存中奖的名称
- 声明一个变量保存 获得卡片的种类
- 遍历数组, 如果卡片的个数 大于 等于1 获得种类加 1 否则 肯定没有中奖 跳出循环
- 判断种类是否有 6 种
-
- 如果等于 6 说明已经集够卡片 中奖结果改为 true
- 罗列所有的奖品名称
- 获取随机下标 0 -- 2
- 获取随机名称
// 奖品状态
@State
jiangPin: string = ''
// 抽奖页面-遮罩层
Column({space: 50}) {
Text('恭喜获得手机一部')
Image($r(`app.media.${this.prize}`))
Button('再来一次')
}
Button('开心收下')
.onClick(() => {
// 判断是否可以抽奖:定义count变量保存已经获得的卡片种类数量(如果每个生肖卡数量>=1,则count+1)
// 如果count == 6,说明已经得到了6种卡,则可以抽奖
// 判断是否中奖
let count: number = 0
for (const item of this.images) {
if (item.num >= 1) {
count += 1
} else {
break
}
}
if (count == 6) {
this.isGet = true
}
// 随机奖品
const arr: string[] = ['hw', 'xm', 'pg']
let randomNum: number = Math.floor(Math.random() * 3)
this.jiangPin = arr[randomNum]
})
4.6. 再次抽奖
单击“再来一次”按钮,重新抽卡
- 关闭遮罩层:中奖状态为 false
- 初始化列表数据
Button('再来一次')
.onClick(() => {
this.isGet = false
this.images = [
{url: 'app.media.bg_00', num: 0},
{url: 'app.media.bg_01', num: 0},
{url: 'app.media.bg_02', num: 0},
{url: 'app.media.bg_03', num: 0},
{url: 'app.media.bg_04', num: 0},
{url: 'app.media.bg_05', num: 0}
]
})