版本:3.4.0
语言:TypeScript
环境: Mac
参考:
简介
ScrollView
组件作为滚动容器来使用,它的实现通过ScrollBar组件来展示内容的位置和Mask组件显示指定区域,来保证有限的区域内显示更多的内容。
在cocosCreator中,滚动容器的实现主要是:ScrollView组件。层级管理器的节点如下:
构成主要有两部分:
- scrollBar 滚动条相关,用于展示内容的位置,编译器默认垂直
- view 内容显示相关,节点内增加了Mask遮罩显示指定区域,通过content增加
item
显示滚动区域
ScrollView
节点属性:
属性 | 功能说明 |
---|---|
Horizontal | 布尔值,是否允许横向滚动 |
HorizontalScrollBar | 节点引用,用来创建一个滚动条来显示 content 在水平方向上的位置 |
Vertical | 布尔值,是否允许纵向滚动 |
VerticalScrollBar | 节点引用,用来创建一个滚动条来显示 content 在垂直方向上的位置 |
Inertia | 滚动的时候是否有加速度 |
Brake | 滚动之后的减速系数,范围[0, 1]。 1 时立马停止滚动, 0时则会一直滚动到 content 的边界 |
Elastic | 布尔值,是否回弹 |
BounceDuration | 回弹所需要的时间,范围[0, 10] |
Content | 节点引用,所有的子节点放到此处,必须存在,否则滚动容器无法实现 |
ScrollEvents | 可用于添加一个Target、Component、handler、 CutomEventData的回调事件 |
CancelInnerEvents | 滚动行为是否会取消子节点上注册的触摸事件,默认为 true |
在cocosCreator中, 滚动容器的使用主要就是:ScrollView
和PageView
。
没有像cocos2d-x所谓的ListView,TableView 的实现,因此很多项目会对ScrollView进行二次封装,用于实现类似于TableView的功能,降低内存占用。
在cocosCreator中ScrollView, PageView 存在着同cocos2d-x一样的问题,加载很多item的时候必定卡顿,常用的优化手段要么是模拟TableView的实现,要么就是分帧加载
基本使用
使用ScrollView
,需要注意:
- scrollBar节点可选,可以通过
Horizontal/HorizontalScrollBar
或Vertical/VerticalScrollBar
设置水平或垂直滚动条相关,如果不需要,可以去掉勾选或者直接删除节点 ScrollView
节点内view可以增加widget
组件,用于UI对齐content
节点内可增加Layout
组件,用于设置水平,垂直,格子布局
注: 不要同时使用Layout和Widget组件,以免产生不可预料的后果
针对于content
下的布局组件Layout
的设置,如下图:
注意设置:
- Type 设置水平,垂直,格子布局类型
- ResizeMode 一般设置为CONTAINER模式,它会自动计算布局的大小,用于滚动使用
- SpacingX/SpacingY 设置item之间的间隔
设置结束后,在脚本中我们可编写如下代码完成ScrollView的基本使用:
@ccclass('UI_DemoLayer')
export class UI_DemoLayer extends Component {
// 获取ScrollView组件
@property(ScrollView) scroll: ScrollView = null;
// 获取预制体item
@property(Prefab) itemPrefab: Prefab = null;
protected onLoad(): void {
// 移除content下的子节点
this.scroll.content.removeAllChildren();
for (let i = 0; i < 10; ++i) {
// 克隆并将节点添加到content中
let itemNode = instantiate(this.itemPrefab);
if (itemNode) {
itemNode.parent = this.scroll.content;
// 更新指定的item
let itemScript = itemNode.getComponent(ScrollItem);
itemScript.updateItem(i);
}
}
}
}
建议: 构建item前,调用
content.removeAllChildren()
接口,用于移除无效节点。
拓展
使用滚动容器,尤其在content
节点内增加 Layout组件 可以很方便的设置滚动的各种类型。
下面的示例是通过脚本代码动态设置滚动视图的各种布局类型,示意图如下:
关于Toggle的使用,可参考博客: Toggle和ToggleContainer的使用。具体的实现代码:
// 布局类型
enum kLayoutType {
HORIZONTAL = 0, // 水平布局
VERTICAL, // 垂直布局
GRID, // 格子布局
};
/*
1. 预制体的大小会随着布局类型的改变而改变,主要用于Demo的实现
2. 为方便预览,水平或垂直布局item数量10个,格子布局为30个
*/
@ccclass('UI_ScrollEffecNormaltLayer')
export class UI_ScrollEffecNormaltLayer extends Component {
// 滚动容器
@property(ScrollView) scroll: ScrollView = null;
// 预制体
@property(Prefab) itemPrefab: Prefab = null;
// 布局类型
private _layoutType: kLayoutType = kLayoutType.HORIZONTAL;
// 可视图大小
private _scrollSize = null;
protected start(): void {
this._scrollSize = this.scroll.getComponent(UITransform).contentSize;
this.refreshScroll();
}
// 更新scroll
private refreshScroll() {
let itemCount = 10;
const content = this.scroll.content;
const contentLayout = content.getComponent(Layout);
const contentTransform = content.getComponent(UITransform);
// 设置滚动
if (this._layoutType === kLayoutType.VERTICAL) {
this.scroll.horizontal = true;
this.scroll.vertical = false;
}
else {
this.scroll.horizontal = false;
this.scroll.vertical = true;
}
// 设置布局相关
if (this._layoutType === kLayoutType.VERTICAL) {
contentLayout.horizontalDirection = Layout.HorizontalDirection.LEFT_TO_RIGHT;
contentLayout.type = Layout.Type.HORIZONTAL;
// 垂直布局固定可视区域的高度
contentTransform.height = this._scrollSize.height;
contentLayout.spacingX = 10;
contentLayout.spacingY = 0;
}
else if (this._layoutType === kLayoutType.HORIZONTAL) {
contentLayout.verticalDirection = Layout.VerticalDirection.TOP_TO_BOTTOM;
contentLayout.type = Layout.Type.VERTICAL;
// 水平布局固定可视区域的宽度
contentTransform.width = this._scrollSize.width;
contentLayout.spacingX = 0;
contentLayout.spacingY = 10;
}
else if (this._layoutType === kLayoutType.GRID) {
// 设置子节点排列方向
contentLayout.verticalDirection = Layout.VerticalDirection.TOP_TO_BOTTOM;
contentLayout.horizontalDirection = Layout.HorizontalDirection.LEFT_TO_RIGHT;
// 设置布局类型
contentLayout.type = Layout.Type.GRID;
// 设置子节点间隔
contentLayout.spacingX = 10;
contentLayout.spacingY = 10;
// 设置布局约束
contentLayout.constraint = Layout.Constraint.FIXED_COL;
// 设置布局约束的限定值
contentLayout.constraintNum = 5;
// 格子固定可视区域的宽度
contentTransform.width = this._scrollSize.width;
// 设置子节点数量
itemCount = 30;
}
// 填充scroll
content.removeAllChildren();
for (let i = 0; i < itemCount; ++i) {
let itemNode = instantiate(this.itemPrefab);
this.refreshItem(i, itemNode);
itemNode.parent = content;
}
// 更新布局
contentLayout.updateLayout();
}
// 更新item
private refreshItem(index: number, itemNode: Node) {
// 不同的布局item的大小会进行改变,用于演示
const transform = itemNode.getComponent(UITransform);
if (this._layoutType === kLayoutType.VERTICAL) {
transform.setContentSize(60, this._scrollSize.height);
}
else if (this._layoutType === kLayoutType.HORIZONTAL) {
transform.setContentSize(this._scrollSize.width, 60);
}
else if (this._layoutType === kLayoutType.GRID) {
transform.setContentSize(70, 70);
}
// title
const label = itemNode.getChildByName("title").getComponent(Label);
label.string = index.toString();
}
}
这个示例一般不会发生在实际的项目开发中,但对于理解ScrollView代码很有帮助!
回调事件
ScrollView滚动视图的事件回调函数主要在scrollEvents
中,它的事件类型主要有:
export enum cocos_ui_scroll_view_EventType {
// 滚动视图滚动到顶部边界事件
SCROLL_TO_TOP = "scroll-to-top",
// 滚动视图滚动到底部边界事件
SCROLL_TO_BOTTOM = "scroll-to-bottom",
// 滚动视图滚动到左边界事件
SCROLL_TO_LEFT = "scroll-to-left",
// 滚动视图滚动到右边界事件
SCROLL_TO_RIGHT = "scroll-to-right",
// 滚动视图滚动开始时发出的事件
SCROLL_BEGAN = "scroll-began",
// 滚动视图滚动结束的时候发出的事件
SCROLL_ENDED = "scroll-ended",
// 滚动视图滚动到顶部边界并且开始回弹时发出的事件
BOUNCE_TOP = "bounce-top",
// 滚动视图滚动到底部边界并且开始回弹时发出的事件
BOUNCE_BOTTOM = "bounce-bottom",
// 滚动视图滚动到左边界并且开始回弹时发出的事件
BOUNCE_LEFT = "bounce-left",
// 滚动视图滚动到右边界并且开始回弹时发出的事件
BOUNCE_RIGHT = "bounce-right",
// 滚动视图正在滚动时发出的事件
SCROLLING = "scrolling",
// 滚动视图自动滚动快要结束的时候发出的事件
SCROLL_ENG_WITH_THRESHOLD = "scroll-ended-with-threshold",
// 当用户松手的时候会发出一个事件
TOUCH_UP = "touch-up"
}
事件的回调主要有三种:
- 通过编译器的
ScrollEvents
设定 - 通过脚本代码的
scrollview.node.on
设定 - 通过脚本代码定义
EventHandler
对象设定
这里,我们使用第二种方式编写示例:
protected onEnable(): void {
this.scroll.node.on(ScrollView.EventType.SCROLLING, this.scrolling, this);
}
protected onDisable(): void {
this.scroll.node.off(ScrollView.EventType.SCROLLING, this.scrolling, this);
}
// 此种方式的回调,参数只会有一个为ScrollView组件
private scrolling(scrollView: ScrollView) {
console.log("------ ScrollView 滚动中")
}
注意:使用编译器设定ScrollEvents
或创建EventHandler
对象,支持参数为三个
public scrollEvent(scroll: ScrollView, eventType: any, customData: any) {
// 返回三个参数,分别对应ScrollView组件,事件类型,自定义数据
}
视图滚动
在项目的开发中,针对于某些功能我们可能需要将视图滚动到指定的位置。
官方为此提供了一些接口用于这些功能的实现,主要有:
/*
@func: 视图内容将在指定时间滚动到底部、顶部、左侧、右侧、左上、右上、左下、右下
@param: timeInSecond 滚动时间,以秒为单位。如果超时,则立即跳到指定边界
@param: attenuated 滚动速度是否衰减,默认为true
*/
scrollToBottom(timeInSecond?: number, attenuated?: boolean): void;
scrollToTop(timeInSecond?: number, attenuated?: boolean): void;
scrollToLeft(timeInSecond?: number, attenuated?: boolean): void;
scrollToRight(timeInSecond?: number, attenuated?: boolean): void;
scrollToTopLeft(timeInSecond?: number, attenuated?: boolean): void;
scrollToTopRight(timeInSecond?: number, attenuated?: boolean): void;
scrollToBottomLeft(timeInSecond?: number, attenuated?: boolean): void;
scrollToBottomRight(timeInSecond?: number, attenuated?: boolean): void;
// 视图滚动到指定的偏移位置
scrollToOffset(offset: math.Vec2, timeInSecond?: number, attenuated?: boolean): void;
// 获取当前滚动偏移量
getScrollOffset(): math.Vec2;
// 获取最大可滚动偏移量
getMaxScrollOffset(): math.Vec2;
// 视图是否滚动指定的百分比位置
scrollTo(anchor: math.Vec2, timeInSecond?: number, attenuated?: boolean): void;
scrollToPercentVertical(percent: number, timeInSecond?: number, attenuated?: boolean): void;
scrollToPercentHorizontal(percent: number, timeInSecond: number, attenuated: boolean): void;
// 是否滚动中
isAutoScrolling(): boolean;
// 停止滚动
stopAutoScroll(): void;
简单的示例:
/*
@func: 视图滚动到底部, 如果在滚动中则停止
@param: duration 持续时间,以秒为单位
@param isAttenuate 滚动速度是否衰减
*/
private scrllToBottom(duration: number, isAttenuate:boolean) {
// 检测视图是否滚动中
if (this.scroll.isAutoScrolling()) {
// 如果视图滚动中,则停止滚动
this.scroll.stopAutoScroll();
return;
}
this.scroll.scrollToBottom(duration, isAttenuate);
}
/*
@func: 视图滚动到指定的索引位置
@param: 目标索引
@notice: 假设可视区域item最大显示三个,如果索引<3则停止滚动
*/
private scrollToIndex(targetIndex: number) {
if (targetIndex < 3) {
return;
}
// 获取content大小
let contentSize = this.scroll.content.getComponent(UITransform).contentSize;
console.log("contentSize:", contentSize.height);
// 获取布局垂直间隔
let layout = this.scroll.content.getComponent(Layout);
let spaceY = layout.spacingY;
// 获取item大小
let itemNode = this.scroll.content.children[0];
let itemSize = itemNode.getComponent(UITransform).contentSize;
// 获取滚动偏移量并进行设置
const curOffset = this.scroll.getScrollOffset();
const offsetY = targetIndex * (itemSize.height + spaceY);
this.scroll.scrollToOffset(new Vec3(0, offsetY, 0));
}
最后祝大家学习生活愉快!