在HarmonyOS 的应用开发中,灵活的布局设计是提升用户体验的关键。ArkUI提供的Grid容器组件和GridItem子组件,为开发者提供了强大的网格布局能力。本文将通过实际案例,展示如何使用Grid与其他容器组件嵌套,实现复杂的混合布局。
场景一:Grid与List相互嵌套使用
方案描述
在本场景中,我们将通过Grid与List的嵌套使用,实现一个第三方服务的目录页面。该页面通过横向List布局展示服务目录,并通过滚动联动,实现与下方功能列表的二级联动效果。
核心代码
首先,我们定义一个grids
类,用于存储Grid布局的数据:
enum ScrollPosition { start, center, end }
class grids {
gridname: string = '';
gridList: string[] = [];
constructor(gridname: string, gridList: string[]) {
this.gridname = gridname;
this.gridList = gridList;
}
}
接下来,我们创建一个TextDemo
组件,用于展示Grid与List的嵌套布局:
@Entry
@Component
struct TextDemo {
@State listPosition: number = ScrollPosition.start;
@State scrollPosition: number = ScrollPosition.start;
@State showTitle: boolean = false;
@State currentYOffset: number = 0;
private scrollerForScroll: Scroller = new Scroller();
private scrollerForList: Scroller = new Scroller();
private scrollerForTitle: Scroller = new Scroller();
@State currentIndex: number = 0;
@State list01: string[] = ['第三方服务', '查询', '财富', '转账', '贷款', '跨境金融'];
@State gridList1: grids[] = [];
aboutToAppear() {
// 初始化数据
this.gridList1.push(new grids('精选', ['理财', '基金', '外汇购入', '朝朝盈', '多宝理财', '银行卡转入', '薪福专区', '网点预约']));
}
@Builder myBuilder() {
Row() {
List({ space: 30, scroller: this.scrollerForTitle }) {
ForEach(this.list01, (item: string, index) => {
ListItem() {
Column() {
Text(item)
.fontSize(15)
.fontWeight(this.currentIndex == index ? FontWeight.Bold : FontWeight.Regular)
}
.height(35)
.onClick(() => {
this.scrollerForList.scrollToIndex(index);
this.scrollerForScroll.scrollEdge(Edge.Bottom);
this.currentIndex = index;
})
}
})
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
}
.backgroundColor(Color.White)
}
build() {
Column() {
Image($r('app.media.img'))
.width('100%')
.height('40')
Column() {
Stack({ alignContent: Alignment.Top }) {
Scroll(this.scrollerForScroll) {
Column() {
GridDemo01({ gridList: new grids('精选', ['理财', '基金', '外汇购入', '朝朝盈', '多宝理财', '银行卡转入', '薪福专区', '网点预约']) })
.backgroundColor(Color.White)
this.myBuilder();
Divider()
.color('#d9d4d4')
.strokeWidth(5)
List({ space: 10, scroller: this.scrollerForList }) {
ForEach(this.gridList1, (item: grids, index) => {
GridDemo01({ gridList: item })
.onVisibleAreaChange([0.8], (isVisible) => {
if (isVisible) {
this.currentIndex = index;
this.scrollerForTitle.scrollToIndex(this.currentIndex);
}
})
})
}
.padding({ left: 10, right: 10 })
.width("100%")
.edgeEffect(EdgeEffect.None)
.scrollBar(BarState.Off)
.onScrollFrameBegin((offset: number, state: ScrollState) => {
if (this.scrollPosition == ScrollPosition.end && (this.listPosition != ScrollPosition.start || offset > 0)) {
return { offsetRemain: offset };
} else {
this.scrollerForScroll.scrollBy(0, offset);
return { offsetRemain: 0 };
}
})
.width("100%")
.height("calc(100% - 40vp)")
.backgroundColor('#F1F3F5')
}
}
.scrollBar(BarState.Off)
.width("100%")
.height("100%")
.onScroll((xOffset: number, yOffset: number) => {
this.currentYOffset = this.scrollerForScroll.currentOffset().yOffset;
if (!((this.scrollPosition == ScrollPosition.start && yOffset < 0) || (this.scrollPosition == ScrollPosition.end && yOffset > 0))) {
this.scrollPosition = ScrollPosition.center
}
})
.onScrollEdge((side: Edge) => {
if (side == Edge.Top) {
this.scrollPosition = ScrollPosition.start
} else if (side == Edge.Bottom) {
this.scrollPosition = ScrollPosition.end
}
})
.onScrollFrameBegin(offset => {
if (this.scrollPosition == ScrollPosition.end) {
return { offsetRemain: 0 };
} else {
return { offsetRemain: offset };
}
})
}
.width('100%')
.height('100%')
.backgroundColor(0xDCDCDC)
}
}
}
}
场景二:Grid与Swiper相互嵌套使用
方案描述
在本场景中,我们将通过Grid与Swiper的嵌套使用,实现一个分页效果的布局。每一页中包含的功能菜单数量存储至分页数组gridColList1
。通过多层遍历存放Grid容器组件的自定义组件来达到分页效果。
核心代码
首先,我们创建一个GridDemo
组件,用于展示Grid与Swiper的嵌套布局:
import { display } from '@kit.ArkUI';
@Entry
@Component
struct GridDemo {
@State message: string = 'Hello World';
@State numberList: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
private swiperController: SwiperController = new SwiperController();
@State gridColList: number = 0;
@State gridColList1: number[] = [10, 30];
@State gridcol: number = 10;
@State swiperDistance: number = 150;
@State displayWidth: number = 0;
onChage() {
console.log('this.gridColList1:', this.gridColList1);
}
aboutToAppear(): void {
const displayData: display.Display = display.getDefaultDisplaySync();
this.displayWidth = px2vp(displayData.width);
console.log('displayWidth:', this.displayWidth)
}
build() {
Row() {
Column() {
Image($r('app.media.text02'))
.width("100%")
.height('40')
Swiper(this.swiperController) {
ForEach(this.gridColList1, (item: number, index) => {
Child({ numberList: this.numberList.slice(index * this.gridColList1[index - 1], item) })
})
}
.onChange(() => {
console.log('swiperDistance=', this.swiperDistance)
console.log('displayWidth=', this.displayWidth)
})
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
animateTo({
duration: 700,
curve: Curve.Smooth,
playMode: PlayMode.Normal,
onFinish: () => {
}
}, () => {
if (extraInfo.currentOffset < 0) {
this.swiperDistance = 250
} else if (extraInfo.currentOffset > 0) {
this.swiperDistance = 150
}
})
})
.onAnimationStart((_: number, targetIndex: number) => {
animateTo({
duration: 700,
curve: Curve.Smooth,
playMode: PlayMode.Normal,
onFinish: () => {
}
}, () => {
if (targetIndex === 0) {
this.swiperDistance = 150;
} else if (targetIndex === 1) {
this.swiperDistance = 250;
}
})
})
.height(this.swiperDistance)