HarmonyOS应用开发01-ArkTS基础知识
HarmonyOS应用开发02-程序框架UIAbility、启动模式与路由跳转
想要实现一个列表,在前面是使用 ForEach 循环 StudentListItem.est 构建的,不会由滑动效果,数据源增加时,会发现现在这个“列表”显示不全,而且也是无法滑动的。对于数据源数量较多、需要分页加载的列表,我们使用 List去实现。
接下来学习使用HarmonyOS-ArkTS语言开发方式中的基本组件,这样我们就可以使用基本组件去实现较为复杂丰富的UI。
Column组件的相关API参考:Column组件
Row组件的相关API参考:Row组件
Column表示沿垂直方向布局的容器。Row表示沿水平方向布局的容器。
在布局容器中,默认存在两根轴,分别是主轴和交叉轴,这两个轴始终是相互垂直的。不同的容器中主轴的方向不一样的。
子组件在主轴方向上的对齐使用justifyContent属性来设置,其参数类型是FlexAlign。FlexAlign定义了以下几种类型:
子组件在交叉轴方向上的对齐方式使用alignItems属性来设置。
Column和Row容器的接口都有一个可选参数space,表示子组件在主轴方向上的间距。
容器组件 | 接口 |
---|---|
Column | Column(value?:{space?: string |
Row | Row(value?:{space?: string |
List组件的相关API参考:List组件
Grid组件的相关API参考:Grid组件
循环渲染(ForEach):循环渲染
在上文Demo中增加 getStudentList2
返回数据数量:
import { DataItemBean } from './DataItemBean';
export class DataModel {
getStudentList2(): Array<DataItemBean> {
let studentList: DataItemBean[] = [
{
"title": "丁程鑫",
"image": "https://c-ssl.duitang.com/uploads/item/201805/06/20180506084619_2svWA.jpeg",
},
{
"title": "贺峻霖",
"image": "https://c-ssl.duitang.com/uploads/blog/202107/05/20210705215458_36c2b.jpg",
},
{
"title": "肖战",
"image": "https://c-ssl.duitang.com/uploads/blog/202112/17/20211217211133_85430.jpeg",
},
...
{
"title": "丁程鑫2",
"image": "https://c-ssl.duitang.com/uploads/item/201805/06/20180506084619_2svWA.jpeg",
},
{
"title": "贺峻霖2",
"image": "https://c-ssl.duitang.com/uploads/blog/202107/05/20210705215458_36c2b.jpg",
},
{
"title": "肖战2",
"image": "https://c-ssl.duitang.com/uploads/blog/202112/17/20211217211133_85430.jpeg",
},
...
];
return studentList;
}
}
export default new DataModel();
再次运行APP,会发现数据充满整个屏幕,而且是不能进行滑动的,数据显示不全。要想可以滑动列表显示全部数据,这时就需要用到现行列表组件。常见的列表有线性列表(List列表)和网格布局(Grid列表)。
列表往往由多个列表项组成,所以我们需要在List组件中使用多个ListItem组件来构建列表,这就会导致代码的冗余。使用循环渲染(ForEach)遍历数组的方式构建列表,可以减少重复代码
在 StudentListPage.ets
代码中:
ForEach(this.studentList2, (item: DataItemBean) => {
StudentListItem({ studentData: item })
}, (item: string) => JSON.stringify(item))
// 列表
List({ space: 16 }) {
ForEach(this.studentList2, (item: DataItemBean) => {
ListItem() {
StudentListItem({ studentData: item })
}
}, (item: string) => JSON.stringify(item))
}
.width('100%')
.height('50%')
运行代码,即可滑动显示所有数据。
List组件子组件ListItem之间默认是没有分割线的,部分场景子组件ListItem间需要设置分割线,这时候您可以使用List组件的divider属性。divider属性包含四个参数:
List({ space: 16 }) {
ForEach(this.studentList2, (item: DataItemBean) => {
ListItem() {
StudentListItem({ studentData: item })
}
}, (item: string) => JSON.stringify(item))
}
.width('100%')
.height('50%')
.divider({ strokeWidth: 3, color: Color.Gray, startMargin: 30, endMargin: 0 })
List组件里面的列表项默认是按垂直方向排列的,如果您想让列表沿水平方向排列,您可以将List组件的listDirection属性设置为Axis.Horizontal。
listDirection参数类型是Axis,定义了以下两种类型:
listDirection(Axis.Vertical)
listDirection(Axis.Horizontal)
使用ForEach渲染网格布局
columnsTemplate
: 设置当前网格布局列的数量,不设置时默认1列。设置columnsTemplate的值为’1fr 1fr 1fr 1fr’,表示这个网格为4列,将Grid允许的宽分为4等分,每列占1份;rowsTemplate
:设置当前网格布局行的数量,不设置时默认1行。rowsTemplate的值为’1fr 1fr 1fr 1fr’,表示这个网格为4行,将Grid允许的高分为4等分,每行占1份。columnsGap
:设置列与列的间距。eg:使用columnsGap设置列间距为10vp.rowsGap
:设置行与行的间距。eg:使用rowsTemplate设置行间距也为10vp。layoutDirection
设置布局的主轴方向。默认值:GridDirection.Row1、网格布局如果使用了固定的行数和列数,则构建出的网格是不可滚动的。
2、有时候因为内容较多,需要通过滚动的方式来显示更多的内容,就需要一个可以滚动的网格布局。只需要设置rowsTemplate和columnsTemplate中的一个即可。
Grid组件的相关API参考:Grid组件
(1)、在 StudentListItem.ets
代码中修改Item布局:
import router from '@ohos.router';
import CommonConstants from '../common/constants/CommonConstants';
import { DataItemBean } from '../viewmodel/DataItemBean';
@Component
export default struct StudentListItem {
@State isChecked: boolean = false;
private studentData?: DataItemBean;
aboutToAppear() {
console.log("DataItemBean", this.studentData.title)
console.log("DataItemBean", this.studentData.image)
}
@Builder checkIcon(icon: Resource) {
Image(icon)
.objectFit(ImageFit.Contain)
.width($r('app.float.checkbox_width'))
.height($r('app.float.checkbox_height'))
.margin($r('app.float.checkbox_margin'))
}
build() {
Column() {
Row() {
Text(this.studentData.title)
.fontColor(this.isChecked ? Color.Red : Color.Black)
.fontSize(this.isChecked ? $r('app.float.item_checked_font_size') : $r('app.float.item_font_size'))
.fontWeight(500)
.opacity(this.isChecked ? 0.5 : 1.0)
.decoration({ type: this.isChecked ? TextDecorationType.LineThrough : TextDecorationType.None })
Blank()
Image($r('app.media.ic_arrow_next'))
.width('30vp')
.height('30vp')
.onClick(() => {
// console.log('Next Click' + this.name);
console.log('Next Click' + this.studentData.title);
console.log('Next Click' + this.studentData.image);
router.pushUrl({
// url: 'pages/StudentDetailPage',
url: CommonConstants.STUDENT_DETAIL_URL,
params: {
// name: this.name,
studentData: this.studentData
}
}).catch((error) => {
console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
})
})
}
.width('100%')
.padding({ left: 10, right: 10 })
if (this.isChecked) {
this.checkIcon($r('app.media.ic_checked'))
} else {
this.checkIcon($r('app.media.ic_unchecked'))
}
}
.borderRadius(10)
.backgroundColor($r('app.color.start_window_background'))
.width('100%')
.height($r('app.float.grid_item_height'))
.padding({ top: 10 })
.justifyContent(FlexAlign.SpaceEvenly)
.onClick(() => {
this.isChecked = !this.isChecked;
})
}
}
(2)、在 StudentListPage.ets
代码中修改List组件为Grid组件:
import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';
import prompt from '@system.prompt';
import { DataItemBean } from '../viewmodel/DataItemBean';
// import DataItemBean from '../viewmodel/DataItemBean';
const TAG = '[StudentListPage]';
@Entry
@Component
export struct StudentListPage {
// private studentList: Array = [];
private studentList2: Array<DataItemBean> = [];
@State backMessage: string = '';
@State isRowModel: boolean = true;
// 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,
// 以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()
// 生命周期回调中接收和解析router.back()传递过来的自定义参数。
onPageShow() {
this.backMessage = router.getParams()?.['backMessage'];
console.log(TAG, 'StudentDetailPage返回数据:StudentListPage => ' + this.backMessage)
if (this.backMessage != undefined && this.backMessage != "") {
this.showToast(this.backMessage)
}
}
aboutToAppear() {
// this.studentList = DataModel.getStudentList();
this.studentList2 = DataModel.getStudentList2();
// this.backMessage = router.getParams()?.['backMessage'];
}
showToast(message: string) {
prompt.showToast({
message: message
})
}
build() {
Navigation() {
Row() {
if (this.isRowModel) {
Grid() {
ForEach(this.studentList2, (item: DataItemBean) => {
GridItem() {
StudentListItem({ studentData: item, isRowModel: false })
}
}, (item: string) => JSON.stringify(item))
}
// .width('90%')
.columnsTemplate('1fr 1fr 1fr')
// .rowsTemplate('1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(10)
// .layoutDirection(GridDirection.Row)
}
.width('90%')
// .margin({ left: 10, right: 10 })
}
.title('学生名单')
.size({ width: '100%', height: '100%' })
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)
.menus(this.NavigationMenus())
.backgroundColor($r('app.color.page_background'))
}
}
Tabs组件的更多属性和参数的使用,可以参考API:Tabs
@Builder装饰器的使用,可以参考:@Builder
使用Tabs组件来实现类似Android开发中的BottomNavigationBar组件效果、TabIndicator效果;
TabContent
的属性设 tabBar
置 TabBar
的显示内容。使用通用属性width和height设置了Tabs组件的宽高,使用barWidth和barHeight设置了TabBar的宽度和高度barMode
设置TabBar布局模式BarMode.Fixed
:所有TabBar平均分配barWidth宽度(纵向时平均分配barHeight高度),页签不可滚动;BarMode.Scrollable
:每一个TabBar均使用实际布局宽度,超过总长度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动。使用Tabs组件接口中的参数barPosition设置页签位置。此外页签显示位置还与vertical属性相关联,vertical属性用于设置页签的排列方向,当vertical的属性值为false(默认值)时页签横向排列,为true时页签纵向排列。
BarPosition.Start
vertical属性方法设置为false(默认值)时,页签位于容器顶部。
vertical属性方法设置为true时,页签位于容器左侧。
BarPosition.End
vertical属性方法设置为false时,页签位于容器底部。
vertical属性方法设置为true时,页签位于容器右侧。
@State currentIndex: number = CommonConstants.STUDENT_LIST_TAB_INDEX
// 设置Tabs控制器 Tabs组件的控制器,用于控制Tabs组件进行页签切换。不支持一个TabsController控制多个Tabs组件
private tabsController: TabsController = new TabsController();
// TabContent的tabBar属性除了支持string类型,还支持使用@Builder装饰器修饰的函数。
// 可以使用@Builder装饰器,构造一个生成自定义TabBar样式的函数,实现上面的底部页签效果
@Builder TabBuilder(title: string, index: number, selectImage: Resource, normalImage: Resource) {
Column() {
Image(this.currentIndex === index ? selectImage : normalImage)
.width($r('app.float.mainPage_baseTab_size'))
.height($r('app.float.mainPage_baseTab_size'))
Text(title)
.margin({ top: $r('app.float.mainPage_baseTab_top') })
.fontSize(this.currentIndex === index ? $r('app.float.main_tab_selected_fontSize')
: $r('app.float.main_tab_normal_fontSize'))
.fontColor(this.currentIndex === index ? $r('app.color.mainPage_selected_color')
: $r('app.color.mainPage_normal_color'))
}
.justifyContent(FlexAlign.Center)
.height($r('app.float.mainPage_barHeight'))
.width(CommonConstants.FULL_WIDTH)
.onClick(() => {
this.currentIndex = index;
// 控制Tabs切换到指定页签
this.tabsController.changeIndex(this.currentIndex);
})
}
MainPage.ets
,初始化TabsController去设置Tabs组件;import CommonConstants from '../common/constants/CommonConstants'
import GalleryPage from '../view/GalleryPage';
import { StudentListPage } from './StudentListPage';
@Entry
@Component
struct MainPage {
@State currentIndex: number = CommonConstants.STUDENT_LIST_TAB_INDEX
// 设置Tabs控制器 Tabs组件的控制器,用于控制Tabs组件进行页签切换。不支持一个TabsController控制多个Tabs组件
private tabsController: TabsController = new TabsController();
// TabContent的tabBar属性除了支持string类型,还支持使用@Builder装饰器修饰的函数。
// 可以使用@Builder装饰器,构造一个生成自定义TabBar样式的函数,实现上面的底部页签效果
@Builder TabBuilder(title: string, index: number, selectImage: Resource, normalImage: Resource) {
Column() {
Image(this.currentIndex === index ? selectImage : normalImage)
.width($r('app.float.mainPage_baseTab_size'))
.height($r('app.float.mainPage_baseTab_size'))
Text(title)
.margin({ top: $r('app.float.mainPage_baseTab_top') })
.fontSize(this.currentIndex === index ? $r('app.float.main_tab_selected_fontSize')
: $r('app.float.main_tab_normal_fontSize'))
.fontColor(this.currentIndex === index ? $r('app.color.mainPage_selected_color')
: $r('app.color.mainPage_normal_color'))
}
.justifyContent(FlexAlign.Center)
.height($r('app.float.mainPage_barHeight'))
.width(CommonConstants.FULL_WIDTH)
.onClick(() => {
this.currentIndex = index;
// 控制Tabs切换到指定页签
this.tabsController.changeIndex(this.currentIndex);
})
}
build() {
Tabs({
barPosition: BarPosition.End,
controller: this.tabsController,
}) {
TabContent() {
StudentListPage()
}
.tabBar(
this.TabBuilder(
CommonConstants.STUDENT_LIST_TITLE, CommonConstants.STUDENT_LIST_TAB_INDEX,
$r('app.media.ic_home_selected'), $r('app.media.ic_home_normal')
))
TabContent() {
GalleryPage()
}
.tabBar(
this.TabBuilder(
CommonConstants.PICTURE_TITLE, CommonConstants.PICTURE_TAB_INDEX,
$r('app.media.ic_checked'), $r('app.media.ic_unchecked')
))
}
.vertical(true)
.scrollable(true) // 设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。默认值:true
.width(CommonConstants.FULL_WIDTH) // 设置Tabs组件宽度
.height(CommonConstants.FULL_HEIGHT) // 设置Tabs组件高度
.backgroundColor(Color.White) // 设置Tabs组件背景颜色
.barWidth(CommonConstants.FULL_WIDTH) // 设置TabBar宽度
.barHeight($r('app.float.mainPage_barHeight')) // 设置TabBar高度
.barMode(BarMode.Scrollable) // Tabs的布局模式有Fixed(默认)和Scrollable两种
.onChange((index: number) => {
// Tabs的布局模式有Fixed(默认)和Scrollable两种
this.currentIndex = index;
})
}
}
其中在 GalleryPage().ets
中先放置一个Text组件显示页面;
@Component
export default struct GalleryPage {
build() {
Column() {
Text('Gallery')
.fontSize('22fp')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor($r('app.color.page_background'))
}
}
设置 Tabs
组件的属性 barPosition: BarPosition.End
,并且设置 .vertical(false)
,就将页签位于容器底部,设置TabBar宽度为 100%
,高度设置为 56vp
@Entry
@Component
struct MainPage {
...
build() {
Tabs({
barPosition: BarPosition.End,
controller: this.tabsController,
}) {
...
}
.vertical(false)
.scrollable(true) // 设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。默认值:true
.width(CommonConstants.FULL_WIDTH) // 设置Tabs组件宽度
.height(CommonConstants.FULL_HEIGHT) // 设置Tabs组件高度
.backgroundColor(Color.White) // 设置Tabs组件背景颜色
.barWidth(CommonConstants.FULL_WIDTH) // 设置TabBar宽度
.barHeight($r('app.float.mainPage_barHeight')) // 设置TabBar高度
.barMode(BarMode.Fixed) // Tabs的布局模式有Fixed(默认)和Scrollable两种
.onChange((index: number) => {
// Tabs的布局模式有Fixed(默认)和Scrollable两种
this.currentIndex = index;
})
}
}
设置 Tabs
组件的属性 barPosition: BarPosition.End
,并且设置 .vertical(true)
,就将页签位于容器底部,设置TabBar宽度为 56vp
,高度设置为100%
@Entry
@Component
struct MainPage {
...
build() {
Tabs({
barPosition: BarPosition.End,
controller: this.tabsController,
}) {
...
}
.vertical(true)
.scrollable(true) // 设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。默认值:true
.width(CommonConstants.FULL_WIDTH) // 设置Tabs组件宽度
.height(CommonConstants.FULL_HEIGHT) // 设置Tabs组件高度
.backgroundColor(Color.White) // 设置Tabs组件背景颜色
.barWidth($r('app.float.mainPage_barHeight')) // 设置TabBar宽度
.barHeight(CommonConstants.FULL_WIDTH) // 设置TabBar高度
.barMode(BarMode.Fixed) // Tabs的布局模式有Fixed(默认)和Scrollable两种
.onChange((index: number) => {
// Tabs的布局模式有Fixed(默认)和Scrollable两种
this.currentIndex = index;
})
}
}
Swiper轮播组件的相关API参考:Swiper组件
滑块视图容器,提供子组件滑动轮播显示的能力
SwiperController
给组件绑定一个控制器,用来控制组件翻页
子组件是否自动播放。默认值:false
loop为false时,自动轮播到最后一页时停止轮播。手势切换后不是最后一页时继续播放。
使用自动播放时播放的时间间隔,单位为毫秒。默认值:3000
是否启用导航点指示器。默认值:true
是否开启循环。默认值:true
设置为true时表示开启循环,在LazyForEach懒循环加载模式下,加载的组件数量建议大于5个。
子组件切换的动画时长,单位为毫秒。默认值:400
是否为纵向滑动。默认值:false
设置子组件与子组件之间间隙。默认值:0
说明:不支持设置百分比。
禁用组件滑动切换功能。默认值:false
设置导航点样式:
left: 设置导航点距离Swiper组件左边的距离。
top: 设置导航点距离Swiper组件顶部的距离。
right: 设置导航点距离Swiper组件右边的距离。
bottom: 设置导航点距离Swiper组件底部的距离。
size: 设置导航点的直径。不支持设置百分比。默认值:6vp。
mask: 设置是否显示导航点蒙层样式。
color: 设置导航点的颜色。
selectedColor: 设置选中的导航点的颜色。
onChange
onChange(event: (index: number) => void)
index:number
, 代表当前显示元素的索引。实现效果:在List组件、Grid组件上面增加Swiper轮播组件,整体滑动:
StudentListPage.ets
中修改:@Builder SwiperBuilder(studentList: Array<DataItemBean>) {
Swiper(this.swiperController) {
ForEach(studentList, (item: DataItemBean) => {
Image(item.image)
.borderRadius($r('app.float.home_swiper_borderRadius'))
.width('100%')
.height('240vp')
.objectFit(ImageFit.Fill)
.onClick(() => {
router.pushUrl({
// url: 'pages/StudentDetailPage',
url: CommonConstants.STUDENT_DETAIL_URL,
params: {
studentData: item
}
}).catch((error) => {
console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
})
})
}, (item: DataItemBean) => JSON.stringify(item))
}
.autoPlay(true)
.indicatorStyle({ mask: true, bottom: '10vp' })
.margin({ top: $r('app.float.home_swiper_margin'), bottom: $r('app.float.home_swiper_margin') })
}
嵌入 Scroll
列表中:
import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';
import prompt from '@system.prompt';
import { DataItemBean } from '../viewmodel/DataItemBean';
import CommonConstants from '../common/constants/CommonConstants';
const TAG = '[StudentListPage]';
@Entry
@Component
export struct StudentListPage {
private studentList2: Array<DataItemBean> = [];
...
// Swiper控制器
private swiperController: SwiperController = new SwiperController();
...
@Builder NavigationMenus() {
Row() {
Toggle({ type: ToggleType.Switch, isOn: true })
.selectedColor(Color.Red)
.switchPointColor(Color.White)
.onChange((isOn: boolean) => {
// this.isListModel = !this.isListModel
this.isListModel = isOn
})
}
}
@Builder SwiperBuilder(studentList: Array<DataItemBean>) {
Swiper(this.swiperController) {
ForEach(studentList, (item: DataItemBean) => {
Image(item.image)
.borderRadius($r('app.float.home_swiper_borderRadius'))
.width('100%')
.height('240vp')
.objectFit(ImageFit.Fill)
.onClick(() => {
router.pushUrl({
// url: 'pages/StudentDetailPage',
url: CommonConstants.STUDENT_DETAIL_URL,
params: {
studentData: item
}
}).catch((error) => {
console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
})
})
}, (item: DataItemBean) => JSON.stringify(item))
}
.autoPlay(true)
.indicatorStyle({ mask: true, bottom: '10vp' })
.margin({ top: $r('app.float.home_swiper_margin'), bottom: $r('app.float.home_swiper_margin') })
}
build() {
Navigation() {
Row() {
Scroll() {
Column() {
// Swiper组件
this.SwiperBuilder(this.studentList2)
// 列表
// List组件子组件ListItem之间默认是没有分割线的,部分场景子组件ListItem间需要设置分割线,
// 这时候可以使用List组件的divider属性。divider属性包含四个参数:
// 1、strokeWidth: 分割线的线宽。
// 2、color: 分割线的颜色。
// 3、startMargin:分割线距离列表侧边起始端的距离。
// 4、endMargin: 分割线距离列表侧边结束端的距离
List({ space: 16 }) {
ForEach(this.studentList2, (item: DataItemBean) => {
ListItem() {
StudentListItem({ studentData: item, isRowModel: true })
}
}, (item: string) => JSON.stringify(item))
}
// .width('90%')
.divider({ strokeWidth: 1, color: Color.Gray, startMargin: 30, endMargin: 0 })
// .listDirection(Axis.Horizontal)
Text('---没有更多了---').fontSize('22vp').margin('30vp')
}
}
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
}
.width('90%')
}
.title('学生名单')
.size({ width: '100%', height: '100%' })
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)
.menus(this.NavigationMenus())
.backgroundColor($r('app.color.page_background'))
}
}
嵌入 Scroll
列表中:
import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';
import prompt from '@system.prompt';
import { DataItemBean } from '../viewmodel/DataItemBean';
import CommonConstants from '../common/constants/CommonConstants';
const TAG = '[StudentListPage]';
@Entry
@Component
export struct StudentListPage {
private studentList2: Array<DataItemBean> = [];
...
// Swiper控制器
private swiperController: SwiperController = new SwiperController();
...
build() {
Navigation() {
Row() {
Scroll() {
Column() {
// Swiper组件
this.SwiperBuilder(this.studentList2)
Grid() {
ForEach(this.studentList2, (item: DataItemBean) => {
GridItem() {
StudentListItem({ studentData: item, isRowModel: false })
}
}, (item: string) => JSON.stringify(item))
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr 1fr')
.columnsGap('10vp')
.rowsGap('10vp')
.height('640vp')
// .layoutDirection(GridDirection.Row)
Text('---没有更多了---').fontSize('22vp').margin('30vp')
}
}
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
}
.width('90%')
// .margin({ left: 10, right: 10 })
}
.title('学生名单')
.size({ width: '100%', height: '100%' })
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)
.menus(this.NavigationMenus())
.backgroundColor($r('app.color.page_background'))
}
}
StudentListPage.ets
完整代码:import DataModel from '../viewmodel/DataModel';
import StudentListItem from '../view/StudentListItem';
import router from '@ohos.router';
import prompt from '@system.prompt';
import { DataItemBean } from '../viewmodel/DataItemBean';
import CommonConstants from '../common/constants/CommonConstants';
// import DataItemBean from '../viewmodel/DataItemBean';
const TAG = '[StudentListPage]';
@Entry
@Component
export struct StudentListPage {
// private studentList: Array = [];
private studentList2: Array<DataItemBean> = [];
// 返回上一层数据
@State backMessage: string = '';
// 是否是List组件模式
@State isListModel: boolean = true;
// Swiper控制器
private swiperController: SwiperController = new SwiperController();
// 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,
// 以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()
// 生命周期回调中接收和解析router.back()传递过来的自定义参数。
onPageShow() {
this.backMessage = router.getParams()?.['backMessage'];
console.log(TAG, 'StudentDetailPage返回数据:StudentListPage => ' + this.backMessage)
if (this.backMessage != undefined && this.backMessage != "") {
this.showToast(this.backMessage)
}
}
aboutToAppear() {
// this.studentList = DataModel.getStudentList();
this.studentList2 = DataModel.getStudentList2();
// this.backMessage = router.getParams()?.['backMessage'];
}
showToast(message: string) {
prompt.showToast({
message: message
})
}
@Builder NavigationMenus() {
Row() {
Toggle({ type: ToggleType.Switch, isOn: true })
.selectedColor(Color.Red)
.switchPointColor(Color.White)
.onChange((isOn: boolean) => {
// this.isListModel = !this.isListModel
this.isListModel = isOn
})
}
}
@Builder SwiperBuilder(studentList: Array<DataItemBean>) {
Swiper(this.swiperController) {
ForEach(studentList, (item: DataItemBean) => {
Image(item.image)
.borderRadius($r('app.float.home_swiper_borderRadius'))
.width('100%')
.height('240vp')
.objectFit(ImageFit.Fill)
.onClick(() => {
router.pushUrl({
// url: 'pages/StudentDetailPage',
url: CommonConstants.STUDENT_DETAIL_URL,
params: {
studentData: item
}
}).catch((error) => {
console.log('Next Click', 'IndexPage push error' + JSON.stringify(error));
})
})
}, (item: DataItemBean) => JSON.stringify(item))
}
.autoPlay(true)
.indicatorStyle({ mask: true, bottom: '10vp' })
.margin({ top: $r('app.float.home_swiper_margin'), bottom: $r('app.float.home_swiper_margin') })
}
build() {
Navigation() {
Row() {
if (this.isListModel) {
Scroll() {
Column() {
// Swiper组件
this.SwiperBuilder(this.studentList2)
// 列表
// List组件子组件ListItem之间默认是没有分割线的,部分场景子组件ListItem间需要设置分割线,
// 这时候可以使用List组件的divider属性。divider属性包含四个参数:
// 1、strokeWidth: 分割线的线宽。
// 2、color: 分割线的颜色。
// 3、startMargin:分割线距离列表侧边起始端的距离。
// 4、endMargin: 分割线距离列表侧边结束端的距离
List({ space: 16 }) {
ForEach(this.studentList2, (item: DataItemBean) => {
ListItem() {
StudentListItem({ studentData: item, isRowModel: true })
}
}, (item: string) => JSON.stringify(item))
}
// .width('90%')
.divider({ strokeWidth: 1, color: Color.Gray, startMargin: 30, endMargin: 0 })
// .listDirection(Axis.Horizontal)
Text('---没有更多了---').fontSize('22vp').margin('30vp')
}
}
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
} else {
Scroll() {
Column() {
// Swiper组件
this.SwiperBuilder(this.studentList2)
Grid() {
ForEach(this.studentList2, (item: DataItemBean) => {
GridItem() {
StudentListItem({ studentData: item, isRowModel: false })
}
}, (item: string) => JSON.stringify(item))
}
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr 1fr')
.columnsGap('10vp')
.rowsGap('10vp')
.height('640vp')
// .layoutDirection(GridDirection.Row)
Text('---没有更多了---').fontSize('22vp').margin('30vp')
}
}
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.Spring)
}
}
.width('90%')
// .margin({ left: 10, right: 10 })
}
.title('学生名单')
.size({ width: '100%', height: '100%' })
.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)
.menus(this.NavigationMenus())
.backgroundColor($r('app.color.page_background'))
}
}
本节梳理了HarmonyOS中使用ArkTs语言开发实现基本组件、容器组件、List组件、Grid组件、轮播组件Swiper以及页签切换组件Tabs等基本组件的使用,实现了 一个可以左右滑动切换视图的 带有顶部轮播Banner的
Star List
,并且通过Toggle({ type: ToggleType.Switch, isOn: true })
组件切换List
视图与Grid
列表视图功能。