1.创建以下三个C++类

(1)UTreeViewItemBase(基类UObject)

UCLASS()
class UTreeViewItemBase : public UObject
{
	GENERATED_BODY()
public:

	UTreeViewItemBase();
	virtual ~UTreeViewItemBase() override;
public:
	FString Id;

	//层级
	int32 Level;

	//标签
	FText Label;

	//与自身对应的Actor
	AActor* SelfActor;

   //SelfActor 的初始材质
   TArray<UMaterialInterface*>OriginalMaterials;

	//子级Item数组
	TArray<UObject*> ChildrenData;

}

该类型为树形图三要素中的Item

①Level:决定Item对应Entry的在树形图中的层级结构,如下图

1-1

②Label:Entry显示的文本

③SelfActor:      Actor->Item->Entry. ItemActorEntry之间的桥梁,如下图

1-2

④ChildrenData:存储本级Item的子级Item对象

(2)UEntryWidgetBase(基类UUserWidget,实现接口IUserObjectListEntry)

UCLASS()
class UEntryWidgetBase : public UUserWidget,public IUserObjectListEntry
{
	GENERATED_BODY()

public:
	//override begin
	virtual void NativeConstruct() override;

	virtual void NativePreConstruct() override;

	virtual void NativeOnInitialized() override;
	//override end
 public:

	//子级entry缩进像素长度
	UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="EntryWidgetBase")
	float SubItemIndentWidth=20.f;

	UCheckBox* CheckBox;

	UTextBlock* TextBlock;

	UHorizontalBox* HorBox;

	UTreeView* TreeView;

 protected:
 	//Entry对应的Item
	UTreeViewItemBase* ListItemObject;
}

该类型为树形图三要素中的Entry

①CheckBox,TextBlock,HorBox控件,如下图

1-3

②ListItemObject:Entry对应的Item

(3)UTreeViewPanelBase(基类UUserWidget)

UCLASS()
class UTreeViewPanelBase : public UUserWidget
{
	GENERATED_BODY()
public:

	UTreeViewPanelBase(const FObjectInitializer& ObjectInitializer);

	//override begin
	virtual void NativeConstruct() override;

	virtual void NativePreConstruct() override;

	virtual bool Initialize() override;
	//override end

   UTreeView* TreeView;
  }

该类型为树形图三要素中的TreeView

2.成员函数说明

(1)UTreeViewItemBase 

类中定义以下成员函数

public:
	//初始化子级
	void InitChildren();

	/**
	 * @brief 设置子级隐藏
	 * @param bHidden true:隐藏;false:显示
	 */
	void SetChildrenHiddenInGame(bool bHidden);

	/**
	 * @brief 缓存对应的模型的初始材质
	 */
	void CacheOriginalMaterials();

	/**
	 * @brief 被选中状态,材质更换
	 */
	void OnSelected();

	/**
	 * @brief 取消选中状态,材质恢复
	 */
	void UnSelected();

	/**
	 * @brief 根据选中的Actor找到其对应的Item
	 * @param SelectedActor 选中的Actor
	 * @param OutItem 对应的item
	 */
	void FindSubItemByGivenActor(AActor* SelectedActor,UObject*&OutItem);

解析:

①InitChildren

void UTreeViewItemBase::InitChildren()
{
	UnSelected();
	//缓存初始材质
	CacheOriginalMaterials();
	//获得所有子级actor
	TArray<AActor*>AttachedActors;
	SelfActor->GetAttachedActors(AttachedActors);
	ChildrenData.Reset();
	//为所有子级actor创建对应的子级item
	for(int i=0;i<AttachedActors.Num();i++)
	{
		if (!AttachedActors[i])
			continue;
		UTreeViewItemBase* Item=NewObject<UTreeViewItemBase>(this);
		Item->Id=FString::Printf(TEXT("%s_%d"),*Id,i);
		Item->Level=Level+1;
		Item->Label=FText::FromString(UKismetSystemLibrary::GetDisplayName(AttachedActors[i]));
		Item->SelfActor=AttachedActors[i];
		Item->InitChildren();
		ChildrenData.Add(Item);
	}
}

该成员函数用于查找SelfActor存在多少个子级Actor,然后为每个子级创建子级Item,并将子级Item子级Actor建立对应关系.

ps:初始化子级时要注意,子级可能也有子级,所以需要通过递归逐级往下初始化

②SetChildrenHiddenInGame

void UTreeViewItemBase::SetChildrenHiddenInGame(bool bHidden)
{
	//设置对应actor的显隐性
	SelfActor->SetActorHiddenInGame(bHidden);
	//递归设置子级显隐性
	for (UObject*& ChildObject:ChildrenData)
	{
		UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(ChildObject);
		if (ChildItem)
		{
			ChildItem->SetChildrenHiddenInGame(bHidden);
		}
	}
}

该成员函数用于控制子级Actor(包含自身)的显隐性,如

2-1

③CacheOriginalMaterials

void UTreeViewItemBase::CacheOriginalMaterials()
{
	//获得static mesh 组件
	UStaticMeshComponent* StaticMeshComp=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
	if (StaticMeshComp && StaticMeshComp->GetStaticMesh())
	{
		TArray<UMaterialInterface*>Materials;
		Materials=StaticMeshComp->GetMaterials();
		OriginalMaterials=Materials;
	}
}

该函数用于缓存SelfActor的初始材质

④OnSelected

void UTreeViewItemBase::OnSelected()
{
	//获得静态网格组件
	UStaticMeshComponent* SMC=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
	if(!SMC) return;

//下面注释部分是从配置文件中读取高亮材质,本篇教程不对配置文件进行赘述,可自行加载一个材质用于实现选中高亮效果,如LoadObject
/******************************
	//获得树形图工具配置文件
	UTreeViewToolSettings* TreeViewToolSettings=UTreeViewToolSettings::GetTreeViewToolSettings();
	//获得配置文件里配置的高亮材质
	TSoftObjectPtr<UMaterialInterface> Material=TreeViewToolSettings->HightLightMaterial.LoadSynchronous();
	if (!Material.IsValid()) return;
***********************************************/

	//将所有材质设置为高亮
	int MaterialNum=SMC->GetNumMaterials();
	for (int i=0;i<MaterialNum;i++)
	{
		UMaterialInstanceDynamic* DMI=SMC->CreateDynamicMaterialInstance(i,Material.Get());
		DMI->SetVectorParameterValue(FName("HightLightColor"),TreeViewToolSettings->Color);
	}
}

该函数实现了点击Entry,使其对应的Actor高亮(被选中效果) ,效果如参考图2-1

⑤UnSelected

void UTreeViewItemBase::UnSelected()
{
	//获得static mesh组件
	UStaticMeshComponent* SMC=Cast<UStaticMeshComponent>(SelfActor->GetComponentByClass(UStaticMeshComponent::StaticClass()));
	if(!SMC) return;

	//重置材质为原始材质
	for (int i=0;i<OriginalMaterials.Num();i++)
	{
		SMC->SetMaterial(i,OriginalMaterials[i]);
	}
	for(UObject* Child:ChildrenData)
	{
		if(UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(Child))
		{
			//递归取消选中状态
			ChildItem->UnSelected();
		}
	}
}

该函数实现了取消选中效果,使其对应的Actor由选中高亮效果恢复到初始材质

⑥FindSubItemByGivenActor

void UTreeViewItemBase::FindSubItemByGivenActor(AActor* SelectedActor, UObject*& OutItem)
{
	if(SelfActor==SelectedActor)
	{
		OutItem=this;
		return;
	}
	for (UObject*&ChildObj:ChildrenData)
	{
		UTreeViewItemBase*ChildItem=Cast<UTreeViewItemBase>(ChildObj);
		ChildItem->FindSubItemByGivenActor(SelectedActor,OutItem);
	}
}

该函数用于查找与给定Actor对应的Item,用于点击Actor反向定位树形图中的Entry,如下图

(2)UEntryWidgetBase

NativeOnInitialized(初始化函数)实现

void UEntryWidgetBase::NativeOnInitialized()
{
	Super::NativeOnInitialized();
	//获得umg 控件
	TextBlock=Cast<UTextBlock>(GetWidgetFromName(FName("Textblock")));
	CheckBox=Cast<UCheckBox>(GetWidgetFromName(FName("CheckBox")));
	HorBox=Cast<UHorizontalBox>(GetWidgetFromName(FName("HorBox")));
	//绑定checkbox勾选状态改变回调
	if(CheckBox)
   {
      CheckBox->OnCheckStateChanged.AddDynamic(this,&UEntryWidgetBase::OnCheckBoxStateChanged);
   }
	//默认勾选状态
	CheckBox->SetCheckedState(ECheckBoxState::Checked);
}

类中定义以下成员函数 

public:

	/**
	 * @brief 推测Entry的勾选状态
	 * @param Item entry对应的Item
	 * @return 勾选状态
	 */
	ECheckBoxState PredictEntryState(UTreeViewItemBase* Item);

	/**
	 * @brief 递归刷新entry的上一级entry状态
	 */
	void RefreshParentEntries();

	/**
	 * @brief 递归刷新子级entry状态
	 * @param State 子级状态受父级entry checkbox控件选中状态影响
	 */
	void RefreshChildrenEntries(ECheckBoxState State);

	/**
	 * @brief 刷新各级Entry状态
	 * @param State checkbox 控件选中状态
	 */
	void RefreshHierarchyState(ECheckBoxState State);

	/**
	 * @brief checkbox控件选中状态更改回调函数
	 * @param bIsChecked 是否勾选
	 */
	UFUNCTION()
	void OnCheckBoxStateChanged(bool bIsChecked);

	/**
	 * @brief 根据entry选中状态切换字体
	 * @param bIsSelected 选中:粗体;未选中:正常字体
	 */
	void SwitchFontBySelectState(bool bIsSelected);

	/**
	 * @brief 根据entry选中状态更新对应模型的高亮效果
	 * @param bIsSelected 选中高亮,未选中恢复正常
	 */
	void SwitchMaterialBySelectState(bool bIsSelected);
protected:

	//IUserObjectListEntry begin
	void NativeOnListItemObjectSet(UObject* ListItemObject) override;

	void NativeOnItemSelectionChanged(bool bIsSelected) override;

	void NativeOnEntryReleased() override;
	//IUserObjectListEntry end

解析:

①PredictEntryState(推测Entry状态)

ECheckBoxState UEntryWidgetBase::PredictEntryState(UTreeViewItemBase* Item)
{
	ECheckBoxState State=ECheckBoxState::Checked;
	//注1:
	int Count=0;
	if (!Item) return State;

	//统计子级Entry勾选checkbox的数量
	if (Item->ChildrenData.Num()>0)
	{
		for (UObject*& ChildData:Item->ChildrenData)
		{
			UEntryWidgetBase* ChildEntry=Cast<UEntryWidgetBase>(TreeView->GetEntryWidgetFromItem(ChildData));:
			if (ChildEntry)
			{
				ECheckBoxState ChildState=ChildEntry->CheckBox->GetCheckedState();
				switch (ChildState)
				{
				case ECheckBoxState::Checked:
					Count++;
					break;
				case ECheckBoxState::Undetermined:
					Count+=10000;
					break;
				default:
					break;
				}
			}
         //entry无效(不显示时)以对应item的actor显隐性为准
			else
			{
				UTreeViewItemBase* ChildItem=Cast<UTreeViewItemBase>(ChildData);
				if (ChildItem)
					Count+=ChildItem->SelfActor->IsHidden()?0:1;
			}
		}
		//注2
		State=Item->ChildrenData.Num()==Count?ECheckBoxState::Checked:ECheckBoxState::Undetermined;
	}
	//没有子级时,以对应item的actor的显隐性为准,显示=checked;隐藏=unchecked
	else
	{
		State=Item->SelfActor->IsHidden()?ECheckBoxState::Unchecked:ECheckBoxState::Checked;
	}
	return State;
}

注1:父级的勾选状态(checkbox)受子级影响.通过对比子级已勾选数量对比子级数量来决定父级的勾选状态.

注2:

子级Entry全部Checked(子级数量==Count):父级Checked

子级Entry部分Checked 或者 全部UnChecked(子级数量!=Count):父级Undetermined

参考如下图2-2

2-2

②RefreshParentEntries(刷新父级Entry状态)

void UEntryWidgetBase::RefreshParentEntries()
{
	if(!TreeView) return;
	//获得父级item和entry
	UTreeViewItemBase* ParentItem=Cast<UTreeViewItemBase>(ListItemObject->GetOuter());
	UEntryWidgetBase* ParentEntry=Cast<UEntryWidgetBase>(TreeView->GetEntryWidgetFromItem(ParentItem));
	if (ParentEntry)
	{
		//推测父级checkbox状态
		ECheckBoxState ParentState=PredictEntryState(ParentItem);
		ParentEntry->CheckBox->SetCheckedState(ParentState);
		//递归刷新更上一级父级
		ParentEntry->RefreshParentEntries();
	}
}

向上递归刷新父级Entry的状态,效果参考2-2

③RefreshChildrenEntries(刷新子级Entry状态)

void UEntryWidgetBase::RefreshChildrenEntries(ECheckBoxState State)
{
	if(!TreeView) return;
	//刷新子级checkbox和启用状态
	for(UObject*& ChildData:ListItemObject->ChildrenData)
	{
		UEntryWidgetBase* Entry=TreeView->GetEntryWidgetFromItem<UEntryWidgetBase>(ChildData);
		//entry有效时设置勾选状态
		if (Entry)
		{
			Entry->CheckBox->SetCheckedState(State);
			Entry->RefreshChildrenEntries(State);
		}
	}
}

遍历+递归更新本级Entry的子级Entry(以及子级的子级)的状态.效果参考2-2

④RefreshHierarchyState(刷新层级结构状态,即子父级一块刷新)

void UEntryWidgetBase::RefreshHierarchyState(ECheckBoxState State)
{
	if(State==ECheckBoxState::Undetermined) return;
  	//根据勾选状态更新对应的Actor的显隐性
	ListItemObject->SetChildrenHiddenInGame(CheckBox->GetCheckedState()==ECheckBoxState::Unchecked);
	RefreshChildrenEntries(State);
	RefreshParentEntries();
}

效果参考2-2

④OnCheckBoxStateChanged(Checkbox勾选状态改变回调)

void UEntryWidgetBase::OnCheckBoxStateChanged(bool bIsChecked)
{
	RefreshHierarchyState(bIsChecked?ECheckBoxState::Checked:ECheckBoxState::Unchecked);
}

该回调函数在本篇开头NativeOnInitialized中绑定.

2-3

⑤SwitchFontBySelectState(选中切换字体)

void UEntryWidgetBase::SwitchFontBySelectState(bool bIsSelected)
{
  	//此处字体选择逻辑仅为示例,实际使用建议将字体公开到蓝图,可在蓝图中进行手动选择
  	UFont* FontObject=nullptr;
  	if(bIsSelected)
  	{
      FontObject=LoadObject<UFont>(nullptr,TEXT("/Engine/EngineFonts/BoldFont"));
  	}
  	else
   {
      FontObject=LoadObject<UFont>(nullptr,TEXT("/Engine/EngineFonts/Roboto"));
   }
	//设置字体
	if(FontObject)
	{
		FSlateFontInfo FontInfo=TextBlock->GetFont();
		FontInfo.FontObject=FontObject;
		TextBlock->SetFont(FontInfo);
	}
}

效果参考2-3

⑥SwitchMaterialBySelectState(选中高亮)

void UEntryWidgetBase::SwitchMaterialBySelectState(bool bIsSelected)
{
	//根据选中状态更换材质
	if (bIsSelected)
	{
		ListItemObject->OnSelected();
	}
	else
	{
		ListItemObject->UnSelected();
	}
}

效果参考2-3

⑦NativeOnListItemObjectSet

void UEntryWidgetBase::NativeOnListItemObjectSet(UObject* ItemObject)
{
	//获取TreeView
	TreeView=Cast<UTreeView>(GetOwningListView());

	//获得对应的Item
	ListItemObject=Cast<UTreeViewItemBase>(ItemObject);

	//设置标签字体,文本内容和字体大小,字体大小与level挂钩,级别越高字体越大
	TextBlock->SetText(ListItemObject->Label);
	FSlateFontInfo FontInfo=TextBlock->GetFont();
	FontInfo.Size=UKismetMathLibrary::MapRangeClamped(ListItemObject->Level,1,6,15,8);
	TextBlock->SetFont(FontInfo);

	//根据Entry的level调整文本缩进长度
	UBorderSlot* BorderSlot=Cast<UBorderSlot>(HorBox->Slot);
	if (BorderSlot)
	{
		BorderSlot->SetPadding(FMargin(ListItemObject->Level*SubItemIndentWidth,0,0,0));
	}

	CheckBox->SetCheckedState(PredictEntryState(ListItemObject));
	RefreshParentEntries();
}

该接口函数触发时会返回Entry对应的Item,根据Item的成员变量Level(层级结构的级别,级树形图中的第n级)来决定Entry的字体大小以及文本缩进长度,如下图2-4

2-4

⑧NativeOnItemSelectionChanged

void UEntryWidgetBase::NativeOnItemSelectionChanged(bool bIsSelected)
{
	//根据选中状态更换字体和材质
	SwitchFontBySelectState(bIsSelected);
	if(bIsSelected)
	{
		ListItemObject->OnSelected();
	}
	else
	{
		ListItemObject->UnSelected();
	}
}

该接口函数在Entry的选中状态改变时触发,效果参考2-3

⑨NativeOnEntryReleased

void UEntryWidgetBase::NativeOnEntryReleased()
{
	SwitchFontBySelectState(false);
	SwitchMaterialBySelectState(false);
}

该接口在Entry被释放的时候触发,将字体和Actor材质还原.

Entry在以下情况会被释放:

1.子级Entry被折叠收起时会触发Release,此外,被展开时会触发NativeOnListItemObjectSet

2.Entry不被绘制时,即在视口中不可见,如随着scrollbox滑动导致看不见,同理,当Entry重新被绘制时,触发NativeOnListItemObjectSet

两种情况效果如下图2-5

2-5

(3)UTreeViewPanelBase

NativeConstruct函数实现

void UTreeViewPanelBase::NativeConstruct()
{
	Super::NativeConstruct();
	TreeView=Cast<UTreeView>(GetWidgetFromName(FName("TreeView")));
	//绑定回调函数
	TreeView->SetOnGetItemChildren<UTreeViewPanelBase>(this,&UTreeViewPanelBase::OnGetItemChildren);
}

类中定义以下成员函数

/**
 * @brief 查找actor的父级
 * @param Actor 要被查找父级的actor
 * @param bFindTopLevelParent 是否查找最上级父级
 */
void FindParentActor(AActor*& GivenActor,bool bFindTopLevelParent=true);

UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void StartSelect();
/**
 * @brief 选中一个actor用于显示其层级结构
 * @param Level 层级级别,
 * @param Actor 选中的Actor
 * @param bUseTopLevelParent 是否使用选中actor的最上级父类作为层级结构起点
 */
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void OnSelectActor(const int32 Level,AActor* Actor,bool bUseTopLevelParent=true);

/**
 * @brief 展开所有Item
 */
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void ExpandAllItems();

/**
 * @brief
 * @param Item 展开单个Item
 */
void ExpandItem(UObject* Item);

/**
 * @brief 收起所有Item
 */
UFUNCTION(BlueprintCallable,Category="TreeViewPanel")
void CollapseAllItem();

/**
 * @brief 刷新树形图
 */
void RefreshTreeView();

/**
 * @brief 获得item children回调函数
 * @param Item
 * @param Children
 */
UFUNCTION()
void OnGetItemChildren(UObject* Item,TArray<UObject*>& Children);

解析:

①FindParentActor

3-1

void UTreeViewPanelBase::FindParentActor(AActor*& GivenActor, bool bFindTopLevelParent)
{
	if(!Acotr) return;
	//获得父级actor
	AActor* ParentActor=Acotr->GetAttachParentActor();
	if (ParentActor)
	{
		Acotr=ParentActor;
		//递归继续查找更上层父级
		if (bFindTopLevelParent)
		{
			FindParentActor(Acotr,bFindTopLevelParent);
		}
	}
}

GivenActor:选中的Actor(例如被鼠标点击到的)

bFindTopLevelParent:

true表示查找层级结构中的最上级,如传入参考图3-1中的Cone,返回Cube

false表示查找结构中的上级,如传入Cone返回Sphere

②StartSelect(选取场景中Actor)

void UTreeViewPanelBase::StartSelect()
{
	APlayerController* PlayerController=GetWorld()->GetFirstPlayerController();
	if(PlayerController->IsInputKeyDown(EKeys::RightMouseButton))
		return;
	FVector Location,Direction;
	if(PlayerController->DeprojectMousePositionToWorld(Location,Direction))
	{
		FHitResult HitResult;
		bool bHit=UKismetSystemLibrary::LineTraceSingle(
			this,
			Location,
			Direction*100000.f+Location,
			ETraceTypeQuery::TraceTypeQuery1,
			true,TArray<AActor*>(),
			EDrawDebugTrace::ForOneFrame,
			HitResult,
			true);
		if (bHit)
		{
			OnSelectActor(1,HitResult.GetActor(),true);
		}
	}
}

③OnSelectActor(选中Actor)

void UTreeViewPanelBase::OnSelectActor(const int32 Level, AActor* Actor, bool bUseTopLevelParent)
{
	//查找模型最上级的层级
	AActor*ParentActor=Actor;
	FindParentActor(ParentActor,bUseTopLevelParent);

	if (!ParentActor) return;
	bool bNeedRebuildTreeView=true;
	if(TreeView->GetListItems().IsValidIndex(0))
	{
		UTreeViewItemBase* RootItem=Cast<UTreeViewItemBase>(TreeView->GetListItems()[0]);
		//注1
		if(RootItem->SelfActor==ParentActor)
		{
			bNeedRebuildTreeView=false;
			UObject* OutItem=nullptr;
			RootItem->FindSubItemByGivenActor(Actor,OutItem);
			if (OutItem)
			{
				TArray<UUserWidget*> EntryWidgets=TreeView->GetDisplayedEntryWidgets();
				for (UUserWidget*& EntryWidget:EntryWidgets)
				{
					UEntryWidgetBase* Entry=Cast<UEntryWidgetBase>(EntryWidget);
					UObject* ItemObj=Entry->GetListItem<UObject>();
               //注2
					TreeView->SetItemSelection(ItemObj,false);
				}
           	//注3
				TreeView->RequestNavigateToItem(OutItem);
				TreeView->SetItemSelection(OutItem,true);
			}
		}
	}
	//重新构建树形图
	if (bNeedRebuildTreeView)
	{
		UE_LOG(LogTemp,Warning,TEXT("Select a new object :%s"),*ParentActor->GetName());
		UTreeViewItemBase* Item=NewObject<UTreeViewItemBase>(this);
		Item->Id="0";
		Item->Level=Level;
		Item->Label=FText::FromString(UKismetSystemLibrary::GetDisplayName(ParentActor));
		Item->SelfActor=ParentActor;
		Item->InitChildren();
		TreeView->ClearListItems();
		TreeView->AddItem(Item);
	}
	//展开所有item
	ExpandAllItems();
}

注1:判断选中的Actor的父级Actor是否是上次选中的(即是否切换目标),如下图3-2

3-2

注2:在未切换目标的情况下,将未被选中的子Entry设为未选择状态(未高亮)

注3:将选中的Entry设为已选中(高亮)

效果如下图3-3

3-3

④ExpandItem(展开单个) ExpandAllItems(展开所有Item)

引擎提供的ExpandAll并不能真正的展开所有的层级结构

3-4

调用引擎提供的ExpandAll仅能展开了一层结构,效果如3-5

3-5

为此我们需要自行实现ExpandAll功能,代码如下

void UTreeViewPanelBase::ExpandItem(UObject* Item)
{
	TreeView->SetItemExpansion(Item,true);
	UTreeViewItemBase* TreeViewItem=Cast<UTreeViewItemBase>(Item);
	for (UObject*& ChildItem:TreeViewItem->ChildrenData)
	{
		if(ChildItem)
		{
        //递归展开当前Item的Children
			ExpandItem(ChildItem);
		}
	}
}

void UTreeViewPanelBase::ExpandAllItems()
{
	//官方提供的expandall节点智能展开一级entry,再深层子级无法展开
	TreeView->ExpandAll();
	//手动展开item
	TArray<UObject*> Items=TreeView->GetListItems();
	if (Items.IsValidIndex(0))
	{
		ExpandItem(Items[0]);
	}
}

⑤CollapseAllItem(折叠收起所有Item)

void UTreeViewPanelBase::CollapseAllItem()
{
	TreeView->CollapseAll();
}

⑥RefreshTreeView(刷新层级结构)

void UTreeViewPanelBase::RefreshTreeView()
{
	if(TreeView->GetListItems().IsValidIndex(0))
	{
		UTreeViewItemBase* RootItem=Cast<UTreeViewItemBase>(TreeView->GetListItems()[0]);
		RootItem->InitChildren();
		TreeView->ClearListItems();
		TreeView->AddItem(RootItem);
		ExpandAllItems();
	}
}

注1:Treeview通过GetListItems获得的Item数组中,第0个元素表示根Item

注2:刷新时清空原有的Item

注3:默认刷新时展开所有层级,可根据自身需求选择,不想展开就是执行ExpandAllItems

⑦OnGetItemChildren

void UTreeViewPanelBase::OnGetItemChildren(UObject* Item,TArray<UObject*>& Children)
{
	UTreeViewItemBase* TreeViewItem=Cast<UTreeViewItemBase>(Item);
	if(!TreeViewItem) return;
	Children=TreeViewItem->ChildrenData;
}

该函数在Treeview执行AddItem时自动触发.


一.创建UMG

1.WBP_EntryWidget

1-1

2.WBP_TreeViewPanel

1-2

代码如下

1-3

解析:

(1)使用右键鼠标进行选择目标

(2)EventOnMouseEnter和EventIOnMouseLeave 事件为了防止鼠标穿透UI选中目标

二.演示

至此树形图功能讲解完毕!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐