STreeView介绍:
STreeView是UE提供的一个控件,其已经实现好一套框架,能够根据给定数据自动生成一个树形结构控件,UE的资源目录(SPathView.h)以及大纲视图(SOutlineTreeView.h)里的树状结构都是用此控件生成的。
STreeView提供了丰富的接口,可以根据需要在构建控件时传入参数即可,这里介绍最简单的使用方法,只需要提供数据(TreeItemsSource)、生成Item方法(OnGenerateRow)、获取某个节点的子节点方法(OnGetChildren),具体是实现给定一个目录,用STreeView实现查看此目录下所有文件和文件夹。如下图所示:
数据:实际上是一个数组,存储的是树所有根节点信息,每个节点需要存储节点需要表示的信息、父节点引用、子节点引用。
生成Item方法:给定一个节点,自定义实现需要显示的内容,返回一个Widget用于Item的显示。一般会根据节点里存储的必要信息去生成文本控件,图片控件等去显示Item。
获取某个节点的子节点方法:给定节点,返回其所有的子节点。这里可以做很多自定义,例如筛选功能就可以在这里根据规则只把所有子节点的符合要求的返回即可。
实例详细步骤:
1、创建C++工程TreeViewDemo。
2、创建自定义TreeView控件。在Source内创建STreeViewDemo类(S版控件)以及TreeViewWidgetDemo类(U版控件),对于为什么创建这两个类可以先了解一下UE Slate相关知识。
Build.cs,添加依赖 “UMG”、 “Slate”、 “SlateCore”、 “EditorStyle”
using UnrealBuildTool;
public class TreeViewDemo : ModuleRules
{
public TreeViewDemo(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG", "Slate", "SlateCore", "EditorStyle" });
PrivateDependencyModuleNames.AddRange(new string[] { });
}
}
STreeViewDemo.h
#pragma once
#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Views/STableViewBase.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Views/STreeView.h"
class FTreeViewInstanceData
{
public:
//用于构造根节点
FTreeViewInstanceData(const FString& InPath);
//用于构造子节点
FTreeViewInstanceData(const FString& InPath, TSharedPtr<FTreeViewInstanceData>&InParent);
//获得条目标志的Icon,用于区分目录还是文件
TSharedPtr<SWidget> CreateIcon() const;
public:
// 目标路径
FString DiskPath;
// 路径最后的名字,如D:\Program Files (x86)\PVS-Studio,Name=PVS-Studio,若路径指向一个文件,则Name是不带扩展名的文件名
FString CleanName;
// 标志DiskPath指向的是一个文件还是目录
bool bIsDirectory;
//记录此节点对应的父节点
TSharedPtr<FTreeViewInstanceData> Parent;
//记录此节点对应的所有子节点
TArray<TSharedPtr<FTreeViewInstanceData>> Children;
};
class STreeViewDemo : public SCompoundWidget
{
SLATE_BEGIN_ARGS(STreeViewDemo)
:_RootPath("")
{}
SLATE_ARGUMENT(FString, RootPath)
SLATE_END_ARGS()
public:
/** Constructs this widget with InArgs */
virtual void Construct(const FArguments& InArgs);
private:
typedef TSharedPtr<FTreeViewInstanceData> FTreeViewInstanceItem;
typedef STreeView<FTreeViewInstanceItem> STreeViewPtr;
//根据根目录路径初始化TreeRootItems
void InitTreeView(const FString& InRootPath);
void ConstructChildrenRecursively(FTreeViewInstanceItem TreeItem);
//创建TreeItem的函数
TSharedRef<ITableRow> GenerateTreeRow(FTreeViewInstanceItem TreeItem, const TSharedRef<STableViewBase>& OwnerTable);
//获取某个节点的子节点信息
void GetChildrenForTree(FTreeViewInstanceItem TreeItem, TArray< FTreeViewInstanceItem >& OutChildren);
private:
//实际的TreeView
TSharedPtr<STreeViewPtr> TreeViewPtr;
//包含所有根节点的数组
TArray<FTreeViewInstanceItem> TreeRootItems;
};
STreeViewDemo.cpp
#include "STreeViewDemo.h"
#include "Misc/Paths.h"
#include "EditorStyleSet.h"
#include "Styling/SlateTypes.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/SBoxPanel.h"
#include "HAL/FileManager.h"
void STreeViewDemo::Construct(const FArguments& InArgs)
{
InitTreeView(InArgs._RootPath);
SAssignNew(TreeViewPtr, STreeViewPtr)
.TreeItemsSource(&TreeRootItems)
.OnGenerateRow(this, &STreeViewDemo::GenerateTreeRow)
.ItemHeight(30)
.SelectionMode(ESelectionMode::Multi)
.OnGetChildren(this, &STreeViewDemo::GetChildrenForTree)
.ClearSelectionOnClick(false)
.HighlightParentNodesForSelection(true);
this->ChildSlot
[
TreeViewPtr.ToSharedRef()
];
}
void STreeViewDemo::InitTreeView(const FString& InRootPath)
{
FTreeViewInstanceItem Root = FTreeViewInstanceItem(new FTreeViewInstanceData(InRootPath));
TreeRootItems.Add(Root);
ConstructChildrenRecursively(Root);
}
void STreeViewDemo::ConstructChildrenRecursively(FTreeViewInstanceItem TreeItem)
{
if (TreeItem.IsValid())
{
//找到此目录下所有文件
TArray<FString> FindedFiles;
FString SearchFile = TreeItem->DiskPath + "/*.*";
IFileManager::Get().FindFiles(FindedFiles, *SearchFile, true, true);
for (auto&element : FindedFiles)
{
//构建子节点
FString FullPath = TreeItem->DiskPath + "/" + element;
FTreeViewInstanceItem Child = FTreeViewInstanceItem(new FTreeViewInstanceData(FullPath, TreeItem));
TreeItem->Children.Add(Child);
if (Child->bIsDirectory)
{
//目录则递归获取其子节点信息
ConstructChildrenRecursively(Child);
}
}
}
}
TSharedRef<ITableRow> STreeViewDemo::GenerateTreeRow(FTreeViewInstanceItem TreeItem, const TSharedRef<STableViewBase>& OwnerTable)
{
check(TreeItem.IsValid());
//根据TreeItem构建一个Item表现的Widget
return
SNew(STableRow<FTreeViewInstanceItem>, OwnerTable)
[
SNew(SBox)
.WidthOverride(200.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
TreeItem->CreateIcon().ToSharedRef()
]
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(STextBlock)
.Text(FText::FromString(TreeItem->CleanName))
.Font(FStyleDefaults::GetFontInfo(20))
]
]
];
}
void STreeViewDemo::GetChildrenForTree(FTreeViewInstanceItem TreeItem, TArray< FTreeViewInstanceItem >& OutChildren)
{
//获取TreeItem的子节点信息
if (TreeItem.IsValid())
{
OutChildren = TreeItem->Children;
}
}
FTreeViewInstanceData::FTreeViewInstanceData(const FString& InPath)
:DiskPath(InPath)
{
CleanName = FPaths::GetBaseFilename(DiskPath);
bIsDirectory = !FPaths::FileExists(DiskPath);
}
FTreeViewInstanceData::FTreeViewInstanceData(const FString& InPath, TSharedPtr<FTreeViewInstanceData>&InParent)
:DiskPath(InPath), Parent(InParent)
{
CleanName = FPaths::GetBaseFilename(DiskPath);
bIsDirectory = !FPaths::FileExists(DiskPath);
}
TSharedPtr<SWidget> FTreeViewInstanceData::CreateIcon() const
{
FSlateColor IconColor = FSlateColor::UseForeground();
const FSlateBrush* Brush = FEditorStyle::GetBrush(bIsDirectory ? "ContentBrowser.AssetTreeFolderOpen" : "ContentBrowser.ColumnViewAssetIcon");
return SNew(SImage)
.Image(Brush)
.ColorAndOpacity(IconColor);
}
TreeViewWidgetDemo.h
#pragma once
#include "CoreMinimal.h"
#include "UMG/Public/Components/Widget.h"
#include "TreeViewWidgetDemo.generated.h"
UCLASS()
class UTreeViewWidget : public UWidget
{
GENERATED_BODY()
public:
UTreeViewWidget(const FObjectInitializer& ObjectInitializer);
UPROPERTY(EditAnywhere, DisplayName = "Root Directory")
FString CurrentRootPath;
protected:
virtual TSharedRef<SWidget> RebuildWidget();
};
TreeViewWidgetDemo.cpp
#include "TreeViewWidgetDemo.h"
#include "STreeViewDemo.h"
UTreeViewWidget::UTreeViewWidget(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
TSharedRef<SWidget> UTreeViewWidget::RebuildWidget()
{
return SNew(STreeViewDemo)
.RootPath(CurrentRootPath);
}
3、编译工程,以DevelopmentEditor启动编辑器。
4、在Content目录创建控件蓝图(命名为TreeViewWidget), 并布局如下图(右)所示,其中TreeViewWidgetDemo控件是我们定义的UTreeViewWidget类型控件,在控制板中输入关键字可搜索到并拖入布局器内。
设置属性:选中TreeViewWidgetDemo控件在右侧面板内输入测试用的路径,如上图(左)。
5、打开关卡蓝图,写入创建并显示蓝图的逻辑,如下图:
6、运行游戏查看结果
附上示例工程,解压即可使用,示例工程使用4.24版引擎。Github链接