UE4 将Slate控件封装成UWidget
前言
UE4 本身提供了许多封装好的 UMG 控件,当然我们可以将自己的 Slate 控件封装到 UMG 中,方便我们在可视化编辑器中搭建我们的界面。
在本文中我会以上一篇文章(UE4 Slate UI框架入门)中的 SMyComponentWidget (一个简单的,只包含一个默认 Slot 的自定义控件)为例,将其封装到 UMG 中。
一、基类选择
首先,我们要搞清楚我们需要继承什么类型去实现我们的 UMG。
一般我们会从 UWidget、UContentWidget 或 UPanelWidget 去派生出我们的 UMG 控件
- UWidget 是最简单的类型,如果不需要通过编辑器去添加子控件,可以直接继承该类型,比如 UImage 就是直接从 UWidget 派生的;
- UContentWidget 是拥有固定数量插槽的控件类型,比如 USizeBox、UButton、UBorder 等都是继承的该类型;
- UPanelWidget 是拥有不固定数量插槽的控件类型,比如 UCanvasPanel、UOverlay、UHorizontalBox 等是继承的该类型;
基类的选择通常会与 Slate 控件的功能相关,但并不是绝对的,主要还是取决于你希望在编辑器中如何去使用它。
因为我的 SMyComponentWidget 控件只有一个插槽,并希望能在编辑器中向控件中添加一个其他子控件,所以在这里我选择继承 UContentWidget 来实现。
二、创建自定义UMG控件
1. 重要方法
无论选择哪种基类,我们都需要重载这么几个函数:
RebuildWidget()
在这里我们会生成我们的 Slate 控件。
TSharedRef<SWidget> UMyCompoundWidget::RebuildWidget()
{
TSharedPtr<SWidget> SlateContent;
// 如果在编辑器中添加了子控件,我们可以通过 GetChildrenCount 获取到对应的 Slot
if (GetChildrenCount() > 0)
{
UWidget* UMGContent = GetContentSlot()->Content;
SlateContent= UMGContent ? UMGContent->TakeWidget() : SNullWidget::NullWidget;
}
else
{
SlateContent = SNew(STextBlock)
.Text(LOCTEXT("NoChildWidget", "没有子控件"));
}
// 创建自定义 Slate 控件
MyWidget = SNew(SMyCompoundWidget, 1.0f)
[
SlateContent.ToSharedRef()
];
return MyWidget.ToSharedRef();
}
SynchronizeProperties()
编译时,会调用该函数,主要用于将属性同步到 Slate 控件的显示上
void UMyCompoundWidget::SynchronizeProperties()
{
Super::SynchronizeProperties();
// 如果希望将 UWidget 中的数据同步到 Slate 显示中,可以在这里添加代码
// MyWidget->SetSomething();
}
ReleaseSlateResources(bool bReleaseChildren)
如果有指向 Slate 控件的智能指针,需要在这里释放,否则可能会导致控件被释放时出现意外的错误
void UMyCompoundWidget::ReleaseSlateResources(bool bReleaseChildren)
{
Super::ReleaseSlateResources(bReleaseChildren);
MyWidget.Reset();
}
2. 其他可选方法
到目前为止,我们的 Slate 控件已经能够在 UMG 编辑器中正常使用了,当然,还有一些可选的函数供我们实现更多内容,我举几个例子,就不一一详细阐述了:
GetPaletteCategory()
如果你希望能在 UMG 控件列表中指定分类中找到你的控件,可以重写这个函数,但需要注意的是,这是一个仅在编辑器下使用的函数,意味着你需要添加WITH_EDITOR
宏的控制;GetSlotClass()
提供了重写自定义 Slot 类型的方法,默认使用的是 UPanelSlot,没有什么可以在编辑面板上修改的内容。
三、示例截图和代码
1. 示例截图
2. 示例代码
MyCompoundWidget.h
/**
* 自定义CompoundWidget
*/
UCLASS()
class UMyCompoundWidget : public UContentWidget
{
GENERATED_BODY()
public:
/** 构建 Slate 控件 */
virtual TSharedRef<SWidget> RebuildWidget() override;
/** 同步属性到显示 */
virtual void SynchronizeProperties() override;
/** 释放 Slate 资源 */
virtual void ReleaseSlateResources(bool bReleaseChildren) override;
#if WITH_EDITOR
/** 指定在 UMG 控件列表中的分类 */
virtual const FText GetPaletteCategory() override;
#endif
protected:
/** 底层 Slate 控件的缓存指针 */
TSharedPtr<SMyCompoundWidget> MyWidget;
};
MyCompoundWidget.cpp
#define LOCTEXT_NAMESPACE "UMG"
TSharedRef<SWidget> UMyCompoundWidget::RebuildWidget()
{
TSharedPtr<SWidget> Content;
// 如果在编辑器中添加了子控件,我们可以通过 GetChildrenCount 获取到对应的 Slot
if (GetChildrenCount() > 0)
{
Content = GetContentSlot()->Content ? GetContentSlot()->Content->TakeWidget() : SNullWidget::NullWidget;
}
else
{
Content = SNew(STextBlock)
.Text(LOCTEXT("NoChildWidget", "没有子控件"));
}
// 创建自定义 Slate 控件
MyWidget = SNew(SMyCompoundWidget, 1.0f)
[
Content.ToSharedRef()
];
return MyWidget.ToSharedRef();
}
void UMyCompoundWidget::SynchronizeProperties()
{
Super::SynchronizeProperties();
}
void UMyCompoundWidget::ReleaseSlateResources(bool bReleaseChildren)
{
Super::ReleaseSlateResources(bReleaseChildren);
MyWidget.Reset();
}
#if WITH_EDITOR
const FText UMyCompoundWidget::GetPaletteCategory()
{
return LOCTEXT("Custom", "Custom");
}
#endif
#undef LOCTEXT_NAMESPACE
总结
总的来说,封装方法还是比较简单的,不过这只是一个几乎没有什么功能的示例。
如果想要做好一个控件,还需要在许多细节地方多下功夫,UE4 源码是一个非常好的学习对象,本文只是提供了一个基础思路。