ArkUI核心功能组件使用

1.Tabs(选项卡)

1.1 概述

Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏。

TabBar是导航页签栏,页面结构如下图所示,根据不同的导航类型,布局会有区别,可以分为底部导航、顶部导航、侧边导航,其导航栏分别位于底部、顶部和侧边。

1.2 基本使用

Tabs使用花括号包裹TabContent,每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性进行配置。代码如下

bash 复制代码
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。

bash 复制代码
Tabs({barPosition: BarPosition.Start}){
      
  ...
}.scrollable(false)

1.5 滚动导航栏

当导航页签栏选项比较多时,可以让导航页签滚动

bash 复制代码
Tabs({ barPosition: BarPosition.Start }) {
  // TabContent的内容:关注、视频、游戏、数码、科技、体育、影视、人文、艺术、自然、军事
  ...
}
.barMode(BarMode.Scrollable)

1.6 自定义导航栏

对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会以"文字+图标"表示页签内容,这种情况下,需要自定义导航页签的样式。

1.6.1自定义导航页签

自定义导航每一个页签包含四部分内容

  1. 页签文字
  2. 页签未选中图片
  3. 页签选中时图片
  4. 页签的索引
    创建一个@Builder tabBuilder(){...}函数,用于构建自定义导航页签内容。
bash 复制代码
/*
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(...)),传入相对应的参数。代码如下

bash 复制代码
@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(索引)切换内容页。

  1. 创建TabController与Tabs关联
bash 复制代码
tabsController: TabsController = new TabsController()

build() {
  Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
    ... 
  }
}
  1. 给自定义页签设置点击事件,调用TabController的changeIndex(索引)切换页签。
1.6.3滑动内容页同步切换页签

在自定义导航栏的情况下,滑动内容页时,页签是不会同步切换的;此时需要监听内容页的改变,手动进行切换;

bash 复制代码
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表示列表中的列表项。

如下图所示,这是一个最简单的列表,每一个列表项显示一个文本

bash 复制代码
@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循环渲染的格式如下

bash 复制代码
ForEach(数据集;(元素)=>{
  //需要虚幻渲染的内容
})

比如我现在想要使用List列表显示中国的所有省会城市。

bash 复制代码
@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 列表样式

  1. 设置列表项间距
bash 复制代码
List({ space: 10 }) {
  // ...
}
  1. 设置列表项分割线
bash 复制代码
List() {
  // ...
}
.divider({
  strokeWidth: 0.5,	//分割线的粗细
  color:Color.Black	//分割线的颜色
})
  1. 设置滚动条
bash 复制代码
List() {
  // ...
}
.scrollBar(BarState.Auto) //默认不显示,滚动时显示,2秒后消失

修改上面的案例,给列表加入自定义样式,代码如下

bash 复制代码
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 复杂列表

案例需求:如下图所示是一个任务列表

  1. 每一个列表项包含:任务标题、任务创建时间、任务选中状态
  2. 点击添加时,添加列项到任务列表
  3. 点击删除时,将处于选中状态的列表项从列表中删除
  4. 点击取消时,列表项处于不可编辑状态(不显示复选框);长按列表项时,列表项处于可编辑状态(显示复选框)
2.5.1复杂列表的设计思路

当一个列表项显示的内容比较多时,一般会将列表项的UI和列表项的数据分离开来。如下图所示,是一个任务列表的列表项;

左图:用一个类来封装列表项的数据;

右图:将列表项的UI用自定义组件来表示;

合并:最后列表项的UI和数据绑定即可

多个列表项:采用ForEach循环渲染列表项就可以了

2.5.2 创建列表项数据模型

分析页面发现,每一个ListItem拥有三个数据,分别是:标题、创建时间、选中状态,根据编辑状态决定是否显示复选框。

bash 复制代码
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根据状态决定显示或者不显示。

bash 复制代码
@Entry
@Component
struct Index {
  //2.创建Item组件,并与Item数据模型绑定
  @State isEditModel: number = 1 //是否编辑(1未编辑、2编辑)
  @Builder
  targetItem(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 循环渲染列表项

分析主页面的布局结构,如下图所示,我们先把中间列表这部分写出来,其他的先放一下后面再完成

bash 复制代码
//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状态。

bash 复制代码
ListItem() {
  this.targetItem(item)
}.gesture(LongPressGesture({ duration: 500 }).onAction((event) => {
  this.isEditModel = 2 //编辑模式
}))
2.5.6 新增/删除列表项

当点击新增按钮时,新增一个列表项;点击删除按钮时,删除选中的列表项。

bash 复制代码
@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张图片,如果每一个轮播数据比较多也可以封装成对象)

● 循环渲染轮播数据

代码如下

bash 复制代码
@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 导航点指示器

bash 复制代码
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)  //选中的颜色
)
相关推荐
初始化8 小时前
JavaFx:生成布局 ViewBinding,告别 @FXML 注解
java·kotlin
小奋斗8 小时前
深入浅出:模板引擎详解
javascript·面试
如此风景8 小时前
Android ComposeUI详解
android
LHX sir8 小时前
巨头撤退,玩家内卷!2025,IoT平台的生死劫与重生路
开发语言·前端·物联网·低代码·ui·前端框架·交互
百锦再8 小时前
Python:AI开发第一语言的全面剖析
java·开发语言·人工智能·python·sql·ai·radis
Kapaseker8 小时前
大师级 Compose 图形编程—AGSL 入门
android·kotlin·opengl
我要打打代码8 小时前
wpf触发器
java·linux·wpf
金銀銅鐵8 小时前
[Java] 浅析 Set.of(...) 方法
java·后端
Undoom8 小时前
当Python遇见高德:基于PyQt与JS API构建桌面三维地形图应用实战
javascript·后端