[UE4]UMG CheckBox Group實現
阿新 • • 發佈:2019-01-10
學習UE4的時候,發現UMG居然沒有CheckBoxGroup,CheckBox分組需要自己管理。相比於Unity3D來說,UE4在UI方面確實欠缺很多。無奈之下只好自己實現一個。
實現效果如下:
編輯介面如下:
程式碼實現如下:
CheckBoxGroup.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/NamedSlot.h"
#include "Components/CanvasPanel.h"
#include "Components/CheckBox.h"
#include "CheckBoxGroup.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCheckBoxGroupStateChangedEvent, const TArray<UCheckBox*>&, childChangedArray);
UCLASS()
class UCheckBoxGroup : public UNamedSlot
{
GENERATED_BODY()
public:
UCheckBoxGroup();
protected :
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CheckBox Group")
bool _allowSwitchOff;
UPROPERTY(BlueprintAssignable, Category = "CheckBox Group")
FOnCheckBoxGroupStateChangedEvent _onStateChangedEvent;
UPanelWidget* _groupRoot;
FScriptDelegate _onStateChangedScriptDelegate;
UCheckBox* _lastCheckedChild;
protected :
virtual void OnWidgetRebuilt() override;
bool InitGroup();
bool InitChild(UCheckBox* child);
UFUNCTION(Category = "CheckBox Group")
void OnStateChanged(bool bIsChecked);
public:
UFUNCTION(BlueprintCallable, Category = "CheckBox Group")
TArray<UCheckBox*> CheckChildsState();
/*
UFUNCTION(BlueprintCallable, Category = "CheckBox Group")
bool SetChildState(int nID, bool status);
*/
UFUNCTION(BlueprintCallable, Category = "CheckBox Group")
bool AddGroupChild(UCheckBox* child);
UFUNCTION(BlueprintCallable, Category = "CheckBox Group")
bool RemoveGroupChild(UCheckBox* child);
};
CheckBoxGroup.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "CheckBoxGroup.h"
UCheckBoxGroup::UCheckBoxGroup()
{
_allowSwitchOff = false;
_groupRoot = nullptr;
_lastCheckedChild = nullptr;
_onStateChangedScriptDelegate.BindUFunction(this, TEXT("OnStateChanged"));
//
_allowSwitchOff = false;
}
void UCheckBoxGroup::OnWidgetRebuilt()
{
_groupRoot = Cast<UPanelWidget>(GetChildAt(0));
if (nullptr != _groupRoot)
{
InitGroup();
}
else
{
//UE_LOG(PeterLog, Error, TEXT("UCheckBoxGroup isn`t have group root!"));
}
}
bool UCheckBoxGroup::InitGroup()
{
if (nullptr == _groupRoot)
{
return false;
}
int childCnt = _groupRoot->GetChildrenCount();
for (int i = 0; i < childCnt; ++i)
{
InitChild(Cast<UCheckBox>(_groupRoot->GetChildAt(i)));
}
return true;
}
bool UCheckBoxGroup::InitChild(UCheckBox* child)
{
if (nullptr == child)
{//
return false;
}
child->OnCheckStateChanged.Add(_onStateChangedScriptDelegate);
return true;
}
void UCheckBoxGroup::OnStateChanged(bool bIsChecked)
{
TArray<UCheckBox*> childChangedArray = CheckChildsState();
if (0 != childChangedArray.Num())
{
_onStateChangedEvent.Broadcast(childChangedArray);
}
}
TArray<UCheckBox*> UCheckBoxGroup::CheckChildsState()
{
TArray<UCheckBox*> childChangedArray;
if (nullptr == _groupRoot)
{
return childChangedArray;
}
int childCnt = _groupRoot->GetChildrenCount();
UCheckBox* childWidget = nullptr;
UCheckBox* newLastCheckedChild = nullptr;
UCheckBox* firstCheckedChild = nullptr;
bool hasLastChecked = false;
bool hasOtherChecked = false;
for (int i = 0; i < childCnt; ++i)
{
childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i));
if (nullptr != childWidget)
{
if (nullptr == firstCheckedChild)
{//儲存第一個
firstCheckedChild = childWidget;
}
if (childWidget->IsChecked())
{
if (childWidget != _lastCheckedChild)
{
if (!hasOtherChecked)
{
newLastCheckedChild = childWidget;
hasOtherChecked = true;
}
else
{//uncheck childWidget,只保留一個選擇項
childWidget->SetCheckedState(ECheckBoxState::Unchecked);
}
childChangedArray.Add(childWidget);
}
else
{
hasLastChecked = true;
}
}
}
}
if (hasOtherChecked)
{//uncheck _lastChild
if (hasLastChecked && nullptr != _lastCheckedChild)
{
_lastCheckedChild->SetCheckedState(ECheckBoxState::Unchecked);
childChangedArray.Add(_lastCheckedChild);//原有選擇項被改變
}
_lastCheckedChild = newLastCheckedChild;
}
else
{
if (!_allowSwitchOff)
{
if (nullptr != _lastCheckedChild)
{
if (!hasLastChecked)
{
_lastCheckedChild->SetCheckedState(ECheckBoxState::Checked);//還原,保留選擇項
}
}
else
{//選擇第一個作為預設的選擇項
if (nullptr != firstCheckedChild)//選擇第一個
{
_lastCheckedChild = firstCheckedChild;
_lastCheckedChild->SetCheckedState(ECheckBoxState::Checked);
childChangedArray.Add(_lastCheckedChild);
}
}
}
else
{
if (nullptr != _lastCheckedChild && 0 == childChangedArray.Num())
{//沒有一個被選擇且改變的情況,肯定是最後一個被uncheck了
childChangedArray.Add(_lastCheckedChild);
}
}
}
return childChangedArray;
}
/*
bool UCheckBoxGroup::SetChildState(int nID, bool status)
{
if (nullptr == _groupRoot)
{
return false;
}
TArray<UCheckBox*> childChangedArray;
int childCnt = _groupRoot->GetChildrenCount();
UCheckBox* childWidget = nullptr;
UCheckBox* findCheckedChild = nullptr;
//查詢子物體
for (int i = 0; i < childCnt; ++i)
{
childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i));
if (nullptr != childWidget && nID == childWidget->ID)
{
findCheckedChild = childWidget;
break;
}
}
if (nullptr == findCheckedChild)
{//未找到該子物體
return false;
}
if (findCheckedChild->IsChecked() != status)
{//判斷值
findCheckedChild->SetCheckedState(status ? ECheckBoxState::Checked : ECheckBoxState::Unchecked);
childChangedArray.Add(findCheckedChild);
}
if (status)
{
_lastCheckedChild = findCheckedChild;
for (int i = 0; i < childCnt; ++i)
{
childWidget = Cast<UCheckBox>(_groupRoot->GetChildAt(i));
if (nullptr != childWidget)
{
if (childWidget != findCheckedChild && childWidget->IsChecked())
{
childWidget->SetCheckedState(ECheckBoxState::Unchecked);
childChangedArray.Add(childWidget);
}
}
}
}
else
{
TArray<UCheckBox*> changedArray = CheckChildsState();
//移除合併時可能重複的項
int findIndex = childChangedArray.Find(findCheckedChild);
if (INDEX_NONE != findIndex)
{
findIndex = changedArray.Find(findCheckedChild);
if (INDEX_NONE != findIndex)
{
changedArray.RemoveAt(findIndex);
}
}
//合併
childChangedArray.Append(changedArray);
}
if (0 != childChangedArray.Num())
{//廣播改變
_onStateChangedEvent.Broadcast(childChangedArray);
}
return true;
}
*/
bool UCheckBoxGroup::AddGroupChild(UCheckBox* child)
{
if (nullptr == _groupRoot || nullptr == child)
{
return false;
}
_groupRoot->AddChild(child);
bool bInit = InitChild(child);
if (!bInit)
{
_groupRoot->RemoveChild(child);
return false;
}
return true;
}
bool UCheckBoxGroup::RemoveGroupChild(UCheckBox* child)
{
if (nullptr == _groupRoot || nullptr == child)
{
return false;
}
return _groupRoot->RemoveChild(child);
}