ArkUI核心功能组件使用
1.Tabs(选项卡)
1.1 概述
Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏。
TabBar是导航页签栏,页面结构如下图所示,根据不同的导航类型,布局会有区别,可以分为底部导航、顶部导航、侧边导航,其导航栏分别位于底部、顶部和侧边。
1.2 基本使用
Tabs使用花括号包裹TabContent,每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性进行配置。代码如下
Tabs() {TabContent() {Text('首页的内容').fontSize(30)}.tabBar('首页')TabContent() {Text('推荐的内容').fontSize(30)}.tabBar('推荐')TabContent() {Text('发现的内容').fontSize(30)}.tabBar('发现')TabContent() {Text('我的内容').fontSize(30)}.tabBar("我的")
}
1.3 导航位置
barPosition 属性用来设置导航的位置,如下图所示
说明
● vertical为false时,tabbar的宽度默认为撑满屏幕的宽度,需要设置barWidth为合适值。
● vertical为true时,tabbar的高度默认为实际内容的高度,需要设置barHeight为合适值。
1.4 禁止滑动切换
控制滑动切换的属性为scrollable,默认值为true,表示可以滑动,若要限制滑动切换页签则需要设置为false。
Tabs({barPosition: BarPosition.Start}){...
}.scrollable(false)
1.5 滚动导航栏
当导航页签栏选项比较多时,可以让导航页签滚动
Tabs({ barPosition: BarPosition.Start }) {// TabContent的内容:关注、视频、游戏、数码、科技、体育、影视、人文、艺术、自然、军事...
}
.barMode(BarMode.Scrollable)
1.6 自定义导航栏
对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会以“文字+图标”表示页签内容,这种情况下,需要自定义导航页签的样式。
1.6.1自定义导航页签
自定义导航每一个页签包含四部分内容
- 页签文字
- 页签未选中图片
- 页签选中时图片
- 页签的索引
创建一个@Builder tabBuilder(){…}函数,用于构建自定义导航页签内容。
/*
title: 页签标题
targetIndex: 页签的索引
selectedImg: 选中时图标
normalImg: 未选中时
*/
//当前选中页签索引
@State currentIndex: number = 0
@Builder
tabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {Column() {Image(this.currentIndex == targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')}.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => {this.currentIndex = targetIndex})
}
在调用TabContent对应.tabBar(this.tabBuilder(…)),传入相对应的参数。代码如下
@Entry
@Component
struct TabsDemo {build() {Tabs({barPosition: BarPosition.End}){TabContent(){Text('首页')}.tabBar(this.tabBuilder('首页',0,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('推荐')}.tabBar(this.tabBuilder('推荐',1,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('热门')}.tabBar(this.tabBuilder('热门',2,$r('app.media.home_selected'),$r('app.media.home_normal')))TabContent(){Text('文化')}.tabBar(this.tabBuilder('文化',3,$r('app.media.home_selected'),$r('app.media.home_normal')))}})
}
如下图所示,此时点击页签,能够实现选中与未选中的变色效果。
但是此时还有一个问题,点击页签时并不能同步切换内容页。
1.6.2 点击页签同步切换内容页
想要实现点击页签同步切换页面,需要让Tabs与TabsController相关联,TabsController是用来控制页面切换的控制器,并调用TabsController的changeIndex(索引)切换内容页。
- 创建TabController与Tabs关联
tabsController: TabsController = new TabsController()build() {Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {... }
}
- 给自定义页签设置点击事件,调用TabController的changeIndex(索引)切换页签。
1.6.3滑动内容页同步切换页签
在自定义导航栏的情况下,滑动内容页时,页签是不会同步切换的;此时需要监听内容页的改变,手动进行切换;
Tabs({ barPosition: BarPosition.End, controller: this.tabController }) {TabContent() {Text('首页内容')}.tabBar(this.tabBuilder('首页', 0, $r('app.media.home_selected'), $r('app.media.home_normal')))...
}.onChange((index) => {this.currentIndex = index
})
效果如下
2.List(列表)
2.1 基本概念
List列表是一种复杂容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。使用列表可以轻松高效地显示结构化、可滚动的信息。
2.2 基本使用
List和ListItem是结合起来使用的,List表示列表容器,而ListItem表示列表中的列表项。
如下图所示,这是一个最简单的列表,每一个列表项显示一个文本
@Entry
@Component
struct CityList {build() {List() {ListItem() {Text('北京').fontSize(24)}ListItem() {Text('杭州').fontSize(24)}ListItem() {Text('上海').fontSize(24)}}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center)}
}
2.3 循环渲染
如果List种ListItem列表项非常多,肯定不能一个一个列举,这个时候需要用到ForEach循环渲染。ForEach循环渲染的格式如下
ForEach(数据集;(元素)=>{//需要虚幻渲染的内容
})
比如我现在想要使用List列表显示中国的所有省会城市。
@Entry
@Component
struct CityList {// 定义一个字符串数组,包含中国所有的省会城市citys: string[] = ["北京","上海","天津","重庆","哈尔滨", // 黑龙江省"长春", // 吉林省"沈阳", // 辽宁省"呼和浩特", // 内蒙古自治区"石家庄", // 河北省"太原", // 山西省"西安", // 陕西省"兰州", // 甘肃省"西宁", // 青海省"银川", // 宁夏回族自治区"乌鲁木齐", // 新疆维吾尔自治区"南宁", // 广西壮族自治区"广州", // 广东省"海口", // 海南省"成都", // 四川省"贵阳", // 贵州省"昆明", // 云南省"拉萨", // 西藏自治区"郑州", // 河南省"济南", // 山东省"南京", // 江苏省"杭州", // 浙江省"合肥", // 安徽省"福州", // 福建省"台北", // 台湾省(注意:政治和实际情况可能有所不同)"南昌", // 江西省"长沙", // 湖南省"武汉",// 湖北省];build() {List() {ForEach(this.citys, (item: string) => {ListItem() {Text(item).fontSize(24)}})}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center)}
}
2.4 列表样式
- 设置列表项间距
List({ space: 10 }) {// ...
}
- 设置列表项分割线
List() {// ...
}
.divider({strokeWidth: 0.5, //分割线的粗细color:Color.Black //分割线的颜色
})
- 设置滚动条
List() {// ...
}
.scrollBar(BarState.Auto) //默认不显示,滚动时显示,2秒后消失
修改上面的案例,给列表加入自定义样式,代码如下
List({ space: 20 }) {ForEach(this.citys, (item: string) => {ListItem() {Text(item).fontSize(24)}})}.backgroundColor('#FFF1F3F5').alignListItem(ListItemAlign.Center) //交叉轴居中显示.divider({ //加入分割线strokeWidth:1, //线条粗细startMargin:20, //线条开始外边距endMargin:20, //线条结尾外边距color:'#dedede'}).scrollBar(BarState.Auto) //默认不显示,滚动时显示,2秒后消失
2.5 复杂列表
案例需求:如下图所示是一个任务列表
- 每一个列表项包含:任务标题、任务创建时间、任务选中状态
- 点击添加时,添加列项到任务列表
- 点击删除时,将处于选中状态的列表项从列表中删除
- 点击取消时,列表项处于不可编辑状态(不显示复选框);长按列表项时,列表项处于可编辑状态(显示复选框)
2.5.1复杂列表的设计思路
当一个列表项显示的内容比较多时,一般会将列表项的UI和列表项的数据分离开来。如下图所示,是一个任务列表的列表项;
左图:用一个类来封装列表项的数据;
右图:将列表项的UI用自定义组件来表示;
合并:最后列表项的UI和数据绑定即可
多个列表项:采用ForEach循环渲染列表项就可以了
2.5.2 创建列表项数据模型
分析页面发现,每一个ListItem拥有三个数据,分别是:标题、创建时间、选中状态,根据编辑状态决定是否显示复选框。
class TargetItemData {title: string //目标标题createTime: string //创建时间checkStatus: boolean //选中状态 //1未选中,2选中constructor(title: string //目标标题, checkStatus: boolean //选中状态) {this.title = title;this.createTime = this.getCurrentTime();this.checkStatus = checkStatus;}//获取当前系统时间getCurrentTime(): string {let date = new Date();let year = date.getFullYear();let month = date.getMonth() + 1;let day = date.getDate();let hours = date.getHours();let minutes = date.getMinutes().toString();if (Number.parseInt(minutes) < 10) {minutes = `0${minutes}`;}let second = date.getSeconds().toString();if (Number.parseInt(second) < 10) {second = `0${second}`;}return `${year}/${month}/${day} ${hours}:${minutes}:${second}`;}
}
2.5.3 创建列表项组件
分析页面,每一个Item组件包括:一个标题Text、一个时间Text、一个复选框CheckBox;其中CheckBox根据状态决定显示或者不显示。
@Entry
@Component
struct Index {//2.创建Item组件,并与Item数据模型绑定@State isEditModel: number = 1 //是否编辑(1未编辑、2编辑)@BuildertargetItem(data: TargetItemData) {Row() {Column() {Text(data.title).width('100%').fontSize(18)Text(`创建时间:${data.createTime}`).fontSize(12).fontColor('#A5A6AA').margin({ top: 8 })}.alignItems(HorizontalAlign.Start).layoutWeight(9)if (this.isEditModel == 2) {Checkbox().select(data.checkStatus).layoutWeight(1).onChange((status) => {data.checkStatus = status})}}.padding({left: 12, right: 12}).backgroundColor(Color.White).height(68).border({ radius: 15 }).margin({ left: 12, right: 12 })}build() {//...}
}
2.5.4 循环渲染列表项
分析主页面的布局结构,如下图所示,我们先把中间列表这部分写出来,其他的先放一下后面再完成
//1.定义Item数据模型
//...@Entry
@Component
struct Index {//2.创建Item组件与Item数据模型绑定//...//3.创建Item数据集,并循环渲染到List容器中@State targets: Array<TargetItemData> = [new TargetItemData('运动', false),new TargetItemData('读书', false),new TargetItemData('听音乐', false),new TargetItemData('看电影', false),new TargetItemData('旅游', false)]build() {Column() {List({ space: 20 }) {ForEach(this.targets, (item: TargetItemData) => {ListItem() {this.targetItem(item)}})}.margin({ top: 20 })}.width('100%').height('100%').backgroundColor('#E9E9EB')}
}
2.5.5 给列表项添加长按事件
给ListItem添加长按事件,检测到手指长按时改变Item状态。
ListItem() {this.targetItem(item)
}.gesture(LongPressGesture({ duration: 500 }).onAction((event) => {this.isEditModel = 2 //编辑模式
}))
2.5.6 新增/删除列表项
当点击新增按钮时,新增一个列表项;点击删除按钮时,删除选中的列表项。
@Entry
@Component
struct Index {//...@State isEditModel: number = 1 //是否编辑(1未编辑、2编辑)build() {Column() {List({ space: 20 }) {//...}.margin({ top: 20 }).layoutWeight(9) //权重9//操作按钮Column() {if (this.isEditModel == 1) {Button('新增').width('80%').onClick(() => {this.targets.push(new TargetItemData('钓鱼',false))})} else {Button('删除').width('80%').onClick(() => {let leftData = this.targets.filter((item) => item.checkStatus == false) this.targets = leftData;this.isEditModel = 1 //设置为未编辑状态})}}.width('100%').layoutWeight(1) //权重1.justifyContent(FlexAlign.Center)}.width('100%').height('100%').backgroundColor('#E9E9EB')}
}
3. Swiper(轮播)
3.1 基本概念
Swiper一般用来显示轮播图,在很多应用的首页都会有轮播图展示广告或者一些重要的信息。如下图所示,轮播展示几张图片。
3.2 基本使用
Swiper的基本使用也非常简单,只需要以下两个步骤即可
● 准备轮播数据(我这里就是4张图片,如果每一个轮播数据比较多也可以封装成对象)
● 循环渲染轮播数据
代码如下
@Entry
@Component
struct Index {//1. 准备轮播数据swiperImages: Resource[] = [$r('app.media.fig1'),$r('app.media.fig2'),$r('app.media.fig3'),$r('app.media.fig4'),]build() {Column() {Swiper() {//2.循环轮播数据ForEach(this.swiperImages, (img: Resource) => {Image(img).borderRadius(16).width('100%')})}.margin({ top: 12 }).autoPlay(true) //自动播放}.margin({left: 12, right: 12})}
}
3.3 导航点指示器
Swiper(){ForEach(this.imageArray,(item:Resource)=>{Image(item).width('100%').height(200).borderRadius(10)})
}
//.indicator(false) true表示有导航点、false表示没有导航点
.indicator(//Indicator.digit() //数字导航指示效果Indicator.dot() //原点导航点.itemWidth(20) //未选中的宽度.selectedItemWidth(20) //选中的宽度.color(Color.Red) //未选中的颜色.selectedColor(Color.Yellow) //选中的颜色
)