HarmonyOS 鸿蒙Next基于Grid实现混合布局
HarmonyOS 鸿蒙Next基于Grid实现混合布局 ArkUI提供了Grid容器组件和子组件GridItem,用于构建网格布局。Grid用于设置网格布局相关参数,GridItem定义子组件相关特征。
下面列举一些Grid组件与其他容器组件嵌套使用的场景案例:
场景一:Grid与list相互嵌套使用。
方案:
- 第三方服务的目录页面通过list横向布局实现,且通过scroll的属性与下面详细的功能属性模块list列表形成二级联动。
- 通过onScrollFrameBegin事件计算实时滚动量,滚动整个页面,使上方精选布局滚动,如果页面已滚动到底部,列表不在顶部或列表有正向偏移量,则使页面上方精选部分自动上滑,功能列表置顶。
- 通过ForEach遍历出来的功能目录,与ForEach遍历出来的详细菜单列表,目录与内容两者的index值一致,两个分属不同的List列表,可以关联不同的Scroll值,可以通过scrollToIndex方法来使两个列表形成二级联动效果。
核心代码
enum ScrollPosition {
start,
center,
end
}
class grids {
gridname: string = '';
gridList: string[] = [];
constructor(gridname: string, gridList: string[]) {
this.gridname = gridname;
this.gridList = gridList;
}
}
@Entry
@Component
struct TextDemo {
@State listPosition: number = ScrollPosition.start; // 0代表滚动到List顶部,1代表中间值,2代表滚动到List底部。
@State scrollPosition: number = ScrollPosition.start; // 0代表滚动到页面顶部,1代表中间值,2代表滚动到页面底部。
@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[] = [];
@State sizeList: number[] = [15];
aboutToAppear() {
... // 初始化数据
}
@Builder myBuilder() {
Row() {
List({ space: 30, scroller: this.scrollerForTitle }) {
ForEach(this.list01, (item: string, index) => {
ListItem() {
Column() {
Text(item)
.fontSize(15)
.fontWeight(this.sizeList[index] == 15 ? FontWeight.Bold : FontWeight.Regular)
}
.height(35)
.onClick(() => {
this.scrollerForList.scrollToIndex(index)
this.scrollerForScroll.scrollEdge(Edge.Bottom)
this.sizeList = [];
this.sizeList[index] = 15;
})
}
})
}
.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);
}
})
}, (item: grids) => item.gridname)
}
.padding({ left: 10, right: 10 })
.width("100%")
.edgeEffect(EdgeEffect.None)
.scrollBar(BarState.Off)
.onReachStart(() => {
this.listPosition = ScrollPosition.start
})
.onReachEnd(() => {
this.listPosition = ScrollPosition.end
})
.onScrollIndex((start: number, end: number, center: number) => {
this.sizeList = [];
this.sizeList[start] = 15;
})
.onScrollFrameBegin((offset: number, state: ScrollState) => {
if (!((this.listPosition == ScrollPosition.start && offset < 0)
|| (this.listPosition == ScrollPosition.end && offset > 0))) {
this.listPosition = ScrollPosition.center
}
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)
}
}
}
}
@Component
struct GridDemo01 {
@State gridList: grids = new grids('', []);
@State isfla: boolean = false;
build() {
Column() {
Grid() {
GridItem() {
Text(this.gridList.gridname)
.fontSize(20)
.height(30)
}
}
Grid() {
ForEach(this.gridList.gridList, (item: string, index) => {
GridItem() {
Column() {
Image($r(`app.media.icons${index + 1}`))
.syncLoad(true)
.width(40)
.height(40)
Text(item)
.fontSize(12)
}
}
})
}
.columnsGap(20)
.height(75 * (this.gridList.gridList.length / 4) + (this.gridList.gridList.length % 4 > 0 ? 75 : 0))
.rowsGap(20)
.scrollBar(BarState.Off)
.columnsTemplate('1fr 1fr 1fr 1fr')
}
}
}
场景二:Grid与swiper相互嵌套使用。
方案:
- Grid与swiper相互嵌套使用,通过多层遍历存放Grid容器组件的自定义组件来达到分页效果。
- 每一页里面功能菜单的数量存储至分页数组gridColList1。根据存入的值遍历拆分总菜单数。
- onGestureSwipe功能为页面跟手滑动过程中,逐帧触发该回调。使用extraInfo.currentOffset大小判断向左向右滑动,在向左向右滑动的时候逐帧修改分页的高度,来形成在滑动下一页的时候分页与下方瀑布流形成联动效果。
- onAnimationStart的效果为当滑动到一半不足以滑动到下一页,高度回弹,能够使高度以动画的效果回弹到未滑动前的高度。
核心代码
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:ArrayList<number> = new ArrayList() // 遍历页数
@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)
WaterFlowDemo()
}
.width('100%')
}
.height('100%')
}
}
@Component
struct Child {
@Prop numberList: number[];
@State hei: number = 120
build() {
Grid() {
ForEach(this.numberList, (item: number) => {
GridItem() {
Column() {
Image($r('app.media.app_icon'))
.width(20)
.height(20)
Text("菜单" + item)
.fontSize(15)
}
}
})
}
.columnsGap(20)
.rowsGap(20)
.scrollBar(BarState.Off)
.columnsTemplate('1fr 1fr 1fr 1fr 1fr')
.height(50 * this.numberList.length / 4)
.animation({
duration: 1000
})
}
}
更多关于HarmonyOS 鸿蒙Next基于Grid实现混合布局的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
@State sizeList: number[] = [15]; this.sizeList = []; this.sizeList[index] = 15; 这个没太看懂,sizeList不是定义的一个数组吗,置空后,可以随意一个index值进行赋值?不会数组越界吗?
更多关于HarmonyOS 鸿蒙Next基于Grid实现混合布局的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙特性就是数组置空后,可以定义任意长度的数组,这边定义了一个index长度的数组,且下标index对应赋值为15(选中字体大小),其他下标取值为undefined,不会数组越界,
针对帖子标题“HarmonyOS 鸿蒙Next基于Grid实现混合布局”的问题,以下是专业且简洁的回答:
在HarmonyOS鸿蒙Next系统中,实现基于Grid的混合布局,主要依赖于鸿蒙提供的布局管理器。Grid布局允许开发者以网格形式组织界面元素,每个网格单元可以容纳一个或多个组件。
要实现混合布局,开发者需要在XML布局文件中定义Grid容器,并设置其列数和行数。随后,在Grid容器内部,可以添加不同类型的组件,如文本、图片、按钮等,以实现混合布局效果。这些组件可以通过设置其gridColumn
和gridRow
属性来确定它们占据的网格位置。
此外,鸿蒙系统还提供了丰富的布局属性和事件处理机制,使得开发者可以灵活地调整组件的大小、位置以及响应用户交互。
在实现过程中,开发者需要注意组件之间的间距、对齐方式以及网格的划分,以确保布局的美观和易用性。
如果开发者在实现过程中遇到具体问题,如组件重叠、布局错乱等,可以检查Grid容器的属性设置、组件的网格定位以及事件处理代码是否正确。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html,