UI 拖拽事件
阅读本文大概需要 15 分钟
本文档介绍了如何制作可进行拖放操作的UI,这常用于实现背包内物品拖拽的效果
UI拖拽事件及相关函数
onDragDetected(InGemotry :Geometry,InPointerEvent:PointerEvent) : DragDropOperation
- 当触发拖拽时返回一次生成的拖拽事件
- InGeometry :Geometry可以用来获取UI的坐标信息
- InPointerEvent:PointerEvent可以用来获取鼠标点击或者滑动的信息
- 注意需要先在onTouch事件中调用detectDrag(dragKey: Keys): EventReply或者detectDragIfPressed(inPointEvent: PointerEvent, dragKey: Keys): EventReply,这代表开始进行检测是否有拖拽操作,这之后才能触发onDragDetected事件
- 在onDragDetected事件里使用newDragDrop(inVisualWidget: Widget, inTag?: string, inPayLoad?: any, inPivot?: DragPivot, inOffset?: Vector2): DragDropOperation来返回新的拖拽事件
- inVisualWidget: Widget 传入所生成的拖拽事件在拖拽时展示的UI,注意拖拽用于展示的UI内容会在之后销毁,因此这里最好使用新建的临时UI
- inTag?: string 传入所生成的拖拽事件的标签文本
- inPayLoad?: any 传入拖拽事件数据信息
- inPivot?: DragPivot 传入拖拽显示UI的锚点
- inOffset?: Vector2 传入拖拽显示UI相对于锚点的偏移的百分比,inPivot和inOffset共同决定了鼠标点击或滑动处与此展示UI的相对位置
示例:
ts
onDrop(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation) : boolean
- 拖拽事件在这个UI完成释放时触发,返回true表示处理完成这次事件;这里可以编写释放UI时想要触发的逻辑
- 拖拽事件所展示的UI会自动销毁,如果仍想要展示此UI需要重新创建
示例:
ts
/**
* 拖拽操作生成事件触发后在这个UI释放完成时
* 返回true的话表示处理了这次事件,不会再往这个UI的下一层的UI继续冒泡这个事件
*/
protected onDrop(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation):boolean {
let UI1=createUIByName("NewUI")
this.uiWidgetBase.rootContent.addChild(UI1)
UI1.position=(new Vector2(absoluteToLocal(InGemotry,InDragDropEvent.screenSpacePosition).x-UI1.size.x*0.5,absoluteToLocal(InGemotry,InDragDropEvent.screenSpacePosition).y-UI1.size.y*0.5))
return true;
}
/**
* 拖拽操作生成事件触发后在这个UI释放完成时
* 返回true的话表示处理了这次事件,不会再往这个UI的下一层的UI继续冒泡这个事件
*/
protected onDrop(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation):boolean {
let UI1=createUIByName("NewUI")
this.uiWidgetBase.rootContent.addChild(UI1)
UI1.position=(new Vector2(absoluteToLocal(InGemotry,InDragDropEvent.screenSpacePosition).x-UI1.size.x*0.5,absoluteToLocal(InGemotry,InDragDropEvent.screenSpacePosition).y-UI1.size.y*0.5))
return true;
}
onDragEnter(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation) : void
- 当有正在拖拽的UI进入这个UI的范围内时触发
示例:
ts
/**
* 拖拽操作生成事件触发后进入这个UI时触发
*/
protected onDragEnter(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation) {
console.warn("onDragEnter"+InDragDropEvent.screenSpacePosition)
}
/**
* 拖拽操作生成事件触发后进入这个UI时触发
*/
protected onDragEnter(InGemotry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation) {
console.warn("onDragEnter"+InDragDropEvent.screenSpacePosition)
}
onDragLeave(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) : void
- 当有正在拖拽的UI离开这个UI的范围内时触发
示例:
ts
/**
* 拖拽操作生成事件触发后离开这个UI时触发
*/
protected onDragLeave(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) {
console.warn("onDragLeave"+InDragDropEvent.screenSpacePosition)
}
/**
* 拖拽操作生成事件触发后离开这个UI时触发
*/
protected onDragLeave(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) {
console.warn("onDragLeave"+InDragDropEvent.screenSpacePosition)
}
onDragCancelled(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) : void
- 拖拽事件没有被完成而是取消时触发
- 如果释放的时候没有触发onDrop事件就结束了,则会触发onDragCancelled;也可以通过函数cancelDragDrop/endDragDrop来取消拖拽事件
示例:
ts
/**
* 拖拽操作生成事件触发后,拖拽事件没有完成而是取消时触发
*/
protected onDragCancelled(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) {
console.warn("onDragCancelled")
}
/**
* 拖拽操作生成事件触发后,拖拽事件没有完成而是取消时触发
*/
protected onDragCancelled(InDragDropEvent:PointerEvent,InDragDropOperation : DragDropOperation) {
console.warn("onDragCancelled")
}
onDragOver(InGeometry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation):boolean
- 当有正在拖拽的UI经过这个UI的范围内时触发,返回true表示处理完成这次事件
示例:
ts
/**
* 拖拽操作生成事件触发后经过这个UI时触发
* 返回true的话表示处理了这次事件,不会再往这个UI的下一层的UI继续冒泡这个事件
*/
protected onDragOver(InGeometry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation):boolean {
console.warn("onDragOver"+InDragDropEvent.screenSpacePosition)
return true;
}
/**
* 拖拽操作生成事件触发后经过这个UI时触发
* 返回true的话表示处理了这次事件,不会再往这个UI的下一层的UI继续冒泡这个事件
*/
protected onDragOver(InGeometry :Geometry,InDragDropEvent:PointerEvent,InDragDropOperation:DragDropOperation):boolean {
console.warn("onDragOver"+InDragDropEvent.screenSpacePosition)
return true;
}
其他相关函数
- UIBehavior类下的detectDrag/detectDragIfPressed函数,用于开始检测是否有拖拽操作
- UIBehavior类下的newDragDrop函数,用于创建新的拖拽事件
- 这三个函数在前文onDragDetected的部分已经介绍过了
示例:
ts
- 还有一些拖拽事件相关的函数,可以用来中断/获取/判断当前的拖拽事件
ts
- DragDropOperation类函数,可用于获取指定UI拖拽事件的信息
ts
UI拖拽事件及相关函数
示例一:制作一张最简单的可拖拽图片
- step.1 新建一个NewUI文件,并把允许拖动的UI内容单独放在这个UI文件里面作为一个自定义UI控件,然后编写脚本
- 在这个例子中,我们想要拖拽的是一张空白图片
- 注意应将Root的对齐方式设置为左上对齐,并且把图片的可见性设置为可见
TypeScript
- step.2 然后在监听释放的另一个UI文件(这个例子中直接使用了DefaultUI文件)的UI脚本内编写释放的逻辑;
- 在这个例子中,我们不需要指定释放在某个UI上触发的逻辑,而是希望在整个屏幕中监听这张空白图片的释放,在释放的位置重新创建一个新的空白图片,并销毁旧的空白图片,因此可以就在新建项目的DefaultUI文件的UI脚本中编写onDrop事件
TypeScript
- step.3 最后我们把空白图片的自定义UI控件(也就是NewUI文件)拖入到DefaultUI文件中;也可以使用动态创建的方法,然后运行游戏,我们就得到了一张可以自由拖拽改变位置的空白图片
示例二:制作一个有拖拽功能的背包面板
- step.1 新建一个BagItem文件,并把允许拖动的UI内容单独放在这个UI文件里面作为一个自定义UI控件
- 这里我们把所有UI内容挂在一个自建的容器下,便于拖拽两个格子交换位置时直接交换整个容器
- 每个格子都要允许被拖拽,和前面的例子一样,使用onDragDetected事件来新建UI拖拽事件
- 需要有在有其他格子被拖拽到这个格子上时,实现两个格子交换位置的功能,这部分逻辑写在onDrop事件里
- 还需要制作一个拖出背包面板并释放就丢弃物品的效果,这部分逻辑写在onDropCancelled事件里
- 注意应将Root的对齐方式设置为左上对齐,并且把图片的可见性设置为可见
TypeScript
- step.2 制作一个简单的背包面板,并且编写手动添加物品格子的逻辑
- 背包面板里的容器需要打开自动布局和网格布局功能,这样动态添加的格子会自动排布;更多关于容器的功能请查看产品手册UI 控件-容器
TypeScript
- 最终实现的效果:
- 请注意:这里只是为了演示拖拽事件的用法,实际上如果想要制作一个有完整功能的背包,不仅背包内每个格子的样式要传递过去,对应的物品功能也要作为信息传递过去,大家可以自行尝试。
注意事项
- 按钮、文本按钮、遮罩按钮等按钮类控件不支持使用UI拖拽事件(如果按到按钮类组件会直接触发click事件,而无法触发拖拽事件),因此请使用图片控件作为可拖拽UI,如果想要监听图片作为可拖拽UI是否被点击可以使用touch事件
- 注意应将被拖拽UI的Root节点的对齐方式设置为左上对齐
- 目前在摄像机滑动区边缘会无法触发onDrop事件,请尽量避免这种情况