grid-hybrid项目 gitee.com/harmonyos_s...
此项目交互场景相对复杂,可作为实际应用交互场景的草稿,进一步完善。譬如百度文库的UI交互。
一、GridNestListIndex页面交互
-- 多种控件的Scroller滚动联动
项目中GridNestListIndex
页面通过以下方式实现了滚动联动效果:
1. 核心组件与状态
Scroll
组件:用于页面的整体滚动。List
组件:用于展示网格项列表。Scroller
对象 :scrollerForScroll
:控制页面整体滚动。scrollerForList
:控制列表滚动。scrollerForTitle
:控制标题栏的横向滚动。
- 状态变量 :
listPosition
:记录列表的滚动位置(顶部、中间、底部)。scrollPosition
:记录页面的滚动位置(顶部、中间、底部)。currentIndex
:记录当前显示的列表项的索引。
2. 滚动联动实现逻辑
(1) 列表滚动触发标题栏联动
onScrollIndex
事件 :- 当列表滚动时,通过
onScrollIndex
获取当前显示的第一个子组件的索引start
。 - 更新
currentIndex
状态,并调用scrollerForTitle.scrollToIndex
同步标题栏的滚动位置。
- 当列表滚动时,通过
(2) 标题栏点击触发列表联动
- 标题栏点击事件 :
- 点击标题栏时,调用
scrollerForList.scrollToIndex
滚动列表到对应索引。 - 同时调用
scrollerForScroll.scrollEdge(Edge.Bottom)
确保页面滚动到底部(避免标题栏遮挡列表)。 - 更新
currentIndex
状态。
- 点击标题栏时,调用
(3) 页面滚动与列表滚动联动
onScrollFrameBegin
事件 :- 当列表滚动时,通过
onScrollFrameBegin
计算偏移量offset
。 - 如果页面已滚动到底部(
scrollPosition === ScrollPosition.end
),且列表不在顶部或向上滚动,则允许列表继续滚动。 - 否则,通过
scrollerForScroll.scrollBy
同步页面的滚动。
- 当列表滚动时,通过
(4) 页面滚动状态同步
onWillScroll
和onScrollEdge
事件 :- 监听页面滚动方向,更新
scrollPosition
状态(顶部、中间、底部)。 - 当页面滚动到顶部或底部时,禁止进一步滚动(通过
onScrollFrameBegin
返回offsetRemain: 0
)。
- 监听页面滚动方向,更新
3. 关键代码片段
-
列表滚动索引同步:
typescript.onScrollIndex((start: number) => { this.currentIndex = start; this.scrollerForTitle.scrollToIndex(this.currentIndex); })
-
标题栏点击联动:
typescript.onClick(() => { this.scrollerForList.scrollToIndex(index); this.scrollerForScroll.scrollEdge(Edge.Bottom); this.scrollerForTitle.scrollToIndex(index); this.currentIndex = index; })
-
页面与列表滚动同步:
typescript.onScrollFrameBegin((offset: number) => { if (this.scrollPosition === ScrollPosition.end && (this.listPosition != ScrollPosition.start || offset > 0)) { return { offsetRemain: offset }; } else { this.scrollerForScroll.scrollBy(0, offset); return { offsetRemain: offset }; } })
4. 总结
通过 Scroller
对象和状态变量的协同工作,GridNestListIndex
实现了以下联动效果:
- 列表滚动时,标题栏自动同步到对应位置。
- 点击标题栏时,列表滚动到对应项,并确保页面布局正确。
- 页面滚动时,动态调整列表的滚动行为,避免冲突。
这种设计确保了用户体验的流畅性和一致性。
-- Grid的样式设置
在 GridComponent.ets
文件中,Grid
组件的行列数、宽高和布局是通过以下方式控制的:
1. 行列数控制
(1) 列数控制
columnsTemplate
属性 :- 通过
CommonConstants.GRID_COLUMNS_TEMPLATE
定义列模板。 - 例如:
'1fr 1fr 1fr 1fr'
表示 4 列,每列宽度均分剩余空间。
- 通过
(2) 行数动态计算
- 动态计算 :
- 行数由
gridData.gridItemList.length
和列数决定。 - 例如:
Math.ceil(this.gridData.gridItemList.length / 4)
表示每行 4 个项,计算总行数。
- 行数由
2. 宽高控制
(1) 宽度
width
属性 :-
默认继承父容器宽度(
CommonConstants.FULL_PERCENT
)。 -
可通过
margin
调整左右边距:typescript.margin({ left: $r('app.float.grid_left_margin'), right: $r('app.float.grid_right_margin') })
-
(2) 高度
height
属性 :-
通过
getGridHeight()
方法动态计算:typescript.height(this.getGridHeight())
-
计算逻辑 :
-
每行高度固定为
68
(网格项高度)。 -
行间距为
8
。 -
上下边距为
12 * 2
。 -
公式:
typescriptgridHeight += Math.ceil(this.gridData.gridItemList.length / 4) * 68; // 行高 gridHeight += (Math.ceil(this.gridData.gridItemList.length / 4) - 1) * 8; // 行间距 gridHeight += 12 * 2; // 上下边距
-
-
3. 间距与对齐
(1) 行间距
rowsGap
属性 :- 通过
$r('app.float.grid_rows_gap_size')
设置行间距。
- 通过
(2) 内边距
padding
属性 :- 通过
$r('app.float.grid_padding_size')
设置内边距。
- 通过
(3) 圆角
borderRadius
属性 :- 通过
$r('app.float.grid_border_radius')
设置圆角。
- 通过
4. 关键代码片段
typescript
Grid() {
ForEach(this.gridData.gridItemList, (item: string) => {
GridItemComponent({ itemName: item })
})
}
.columnsTemplate(CommonConstants.GRID_COLUMNS_TEMPLATE) // 列模板
.margin({ left: $r('app.float.grid_left_margin'), right: $r('app.float.grid_right_margin') }) // 左右边距
.height(this.getGridHeight()) // 动态高度
.rowsGap($r('app.float.grid_rows_gap_size')) // 行间距
.padding($r('app.float.grid_padding_size')) // 内边距
.borderRadius($r('app.float.grid_border_radius')) // 圆角
5. 总结
- 列数 :由
columnsTemplate
定义(如 4 列)。 - 行数:动态计算(根据数据项数和列数)。
- 宽度 :继承父容器,通过
margin
调整边距。 - 高度:动态计算(行高 + 行间距 + 边距)。
- 间距与样式 :通过
rowsGap
、padding
等属性控制。
这种设计确保了网格布局的灵活性和响应性。
二、GridNestSwiperIndex页面交互
在 GridNestSwiperIndex.ets
文件中,页面通过 Swiper
和 WaterFlow
组件的协同工作,实现了一个动态的混合布局效果。以下是页面的完整解析:
1. 页面结构与功能
(1) 整体布局
- 顶层容器 :
Column
纵向排列,包含Swiper
(顶部滑动菜单)和WaterFlow
(下方瀑布流内容)。 - 交互逻辑 :
Swiper
切换分类时,动态调整自身高度,间接影响WaterFlow
的展示空间。WaterFlow
根据数据源和分区配置,动态渲染内容项。
(2) 核心组件
组件 | 作用 | 数据依赖 | 动态控制点 |
---|---|---|---|
Swiper |
横向滑动菜单 | GRID_COL_LIST |
swiperDistance (高度) |
WaterFlow |
瀑布流内容展示 | WATER_FLOW_DATA |
sections (分区配置) |
2. 数据驱动设计
(1) Swiper
分页与高度
-
分页数据 :
GRID_COL_LIST
定义分页内容和数量,例如:typescriptGRID_COL_LIST = [ ["推荐", "热门"], // 第一页 ["最新", "分类"] // 第二页 ]
-
高度控制 :
通过滑动事件 (onGestureSwipe
) 和动画回调 (onAnimationStart
) 动态设置swiperDistance
:typescript.onGestureSwipe((index: number) => { this.swiperDistance = (index === 0) ? SWIPER_HEIGHT_SIZE2 : SWIPER_HEIGHT_SIZE1; })
(2) WaterFlow
分区与内容
-
数据源 :
WATER_FLOW_DATA
提供内容项数据(如图片资源),通过WaterFlowDataSource
封装。 -
分区配置 :
sections
定义布局规则,例如:typescriptsections = [ { itemsCount: 1, crossCount: 1 }, // 单栏Banner { itemsCount: 6, crossCount: 2 } // 双栏商品列表 ]
-
动态渲染 :
使用LazyForEach
按需生成FlowItem
:typescriptLazyForEach(this.waterFlowDataSource, (item: Resource) => { FlowItem() { Image(item) // 绑定数据 } })
3. 关键交互流程
-
初始化:
- 加载
GRID_COL_LIST
和WATER_FLOW_DATA
。 - 配置
sections
分区。
- 加载
-
用户滑动
Swiper
:- 触发
onGestureSwipe
→ 更新swiperDistance
。 - 动画过渡高度变化。
- 触发
-
WaterFlow
响应:- 高度变化后,
layoutWeight(1)
自动调整内容区域空间。 - 根据当前分区重新布局项。
- 高度变化后,
4. 设计优势与扩展性
(1) 优势
- 性能优化 :
LazyForEach
懒加载 + 动画平滑过渡。 - 灵活配置 :通过修改
sections
或数据源即可调整布局。
(2) 扩展建议
- 动态数据 :通过接口异步更新
GRID_COL_LIST
和WATER_FLOW_DATA
。 - 高度自适应 :根据
WaterFlow
内容高度动态计算swiperDistance
。
如果需要进一步调整或扩展功能,请具体说明需求!